閉包是...

首先引用 MDN 文檔的一句話做爲開頭git

閉包是函數和聲明該函數的詞法環境的組合。

閉包的概念

當一個函數被 return 的時候,這個函數內部的詞法做用域中的變量是能夠被外界訪問到的,外層函數執行完畢時被銷燬,但因爲內部函數做爲值返回出去,這些值得以保存下來,存儲在內存中,也就是私有性。github

一個基本的例子:閉包

// 來自 MDN
function makeFunc() {
  var name = "DOG";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

閉包是由函數以及建立該函數的詞法環境組合而成。這個環境包含了這個閉包建立時所能訪問的全部局部變量。執行 makeFunc 時建立的 displayName 函數實例的引用,而 displayName 實例仍可訪問其詞法做用域中的變量,便可以訪問到 name 。由此,當 myFunc 被調用時,name 仍可被訪問。函數

閉包的應用

私有屬性

在 JavaScript 中,是沒有原生支持私有屬性的(聽說如今已經有了提議),可是咱們可使用閉包來建立一個私有屬性。code

// 來自 MDN
var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

var counter = makeCounter();

console.log(counter.privateCounter); // undefined
console.log(counter.value()); // 0

存儲變量

function func() {
  var x = 100;
  return {
    function() {
      return x;
    }
  }
}
var m = func(); //運行函數,變量 x 被 m 引用

此時 m 引用了變量 x ,因此函數執行後 x 不會被釋放,能夠把比較重要或者計算耗費很大的值存在 x 中,只須要第一次計算賦值後,就能夠經過 m 函數引用 x 的值,沒必要重複計算,同時也不容易被修改。ip

致使的問題

看一個例子:內存

var a = [];
for(var i = 0; i < 10; i++) {
  a[i] = () => {
    return i;
  };
}
a[6](); //10

在這個簡單的函數中,變量 i 是在 for 循環中定義的,若是是在 C++ 或者 Java 中,這樣定義的變量,一旦循環結束,變量也就隨之銷燬,i 的做用範圍只在循環這個小塊,就稱爲塊級做用域。在 JavaScript中,沒有這樣的塊級做用域。因此上一段代碼其實至關於:作用域

var a = [];
var i = 0;
for(; i < 10; i++) {
  a[i] = () => {
    return i;
  };
}
a[6](); //10

這樣就很好理解了。因爲匿名函數裏面沒有 i 這個變量,在函數執行的時候,這個 i 他要從父級函數中尋找,而父級函數中的 ifor 循環中,當找到這個 i 的時候,是 for 循環已經循環完畢,因此所得與預想不一致。rem

改進:文檔

var a = [];
for(var i = 0; i < 10; i++) {
  a[i] = (() => {
    return i;
  })();
}
a[6]; // 6

總結

看到了一段頗有用的話:

當一個子函數被建立時,是父函數的執行致使的,因此當子函數建立時,父函數已經處於執行階段,因此父函數的執行上下文已經建立了。同時,由於子函數也在父函數的局部變量做用域內,因此,子函數在建立的時候,除了要引用全局上下文,也須要引用父函數的執行上下文。當一個子函數執行時,由於它一樣是函數,因此它一樣須要建立本身的執行上下文,當它返回的時候,一樣也只解除屬性中對自身執行上下文的引用,對父函數的執行上下文的引用並無解除,這意味着,父函數的執行上下文與子函數自己共存亡了。只要子函數還存在引用,垃圾收集器就不會銷燬它們所在的執行上下文。另外,由於父函數的局部變量並不在全局上下文中,因此它只能在子函數的變量解析中被訪問,天然而然就至關於它們是子函數私有的了。
相關文章
相關標籤/搜索