總結:閉包的核心是[[scope]]屬性,在函數解析過程當中,若是函數引用了外層函數的變量,那麼外層函數(即便自身被銷燬)的活動對象帶着對應變量將會被保留,而且記錄在scope屬性中,做爲做用域鏈的第二層,若是還引用了外層函數的外層函數的變量,那麼對應的活動對象與變量也會被保留,並記錄,將會做爲做用域鏈的第三層,依次類推...。當函數被調用時,所取到的外部變量的值將會是調用時各個變量的值。即當前值。閉包調用時也是普通函數,只不過做用域鏈多了閉包Closure的成分 數組
閉包的用途:1.模仿塊級做用域,在當即調用函數中聲明內部須要使用的變量;2.管理私有變量和方法;3.函數柯里化閉包
來個極端的例子函數
function closure() { let result = [], count = 1 setInterval(() => {count++}, 1000) function outer() { let doufu = 'wu' return function inner() { let foo = 'foo' return function closureCallback() { let bar = {bar: 'bar'}, bar1 = 'bar' result[i] = function () { console.log(count) let b = doufu return bar.bar + i } } } } let inner = outer() let closureCallback = inner() for (var i=0; i<10; i++) { // [[scope]]包含closure{result, i} // 函數在這裏當即調用 // i爲實時值0,1,2,3... closureCallback() } return result }
從圖中能夠看到,最終返回結果分別引用了closureCallback, outer,closure中的變量,因此,在做用域鏈中會保存這三個函數的活動對象,不一樣時間調用,返回的count值不一樣,說明引用的是當前值。
如下是手工示意圖學習
如下是個人學習過程
JavaScript裏面的閉包,指的是函數與聲明該函數的詞法環境的組合。
通常在函數裏面聲明函數,而且引用外面函數的變量,就會產生閉包。定義在全局的時候,默認不產生閉包(所謂閉包,就是當內部函數引用了外部函數中的變量時,會在函數的做用域上面添加一層,這一層包含了函數所引用的外部函數的變量,存放在scope屬性裏面,在調用時,用於造成做用域鏈)
函數在執行時,會在內存中建立一個活動對象,該活動對象包含arguments以及一些參數。並經過複製[[scope]]屬性中的對象構建起執行環境的做用域鏈。this
var bar = 'zoo' function Foo() { this.bar = bar }
function foo() { let bar = 'zoo' function zoo() { let zoo = bar } } foo()
function closure() { let result = [] for (var i=0; i<10; i++) { result[i] = function () { return i } } return result } let arr = closure()
主要體如今函數返回函數,函數A在調用函數B時被建立並從B函數的內部返回。當咱們調用A函數的時候,B函數的做用域鏈已經從內存中銷燬,可是咱們仍然能夠在A中訪問B中存在的變量。由於B中的變量仍然保存在A的活動對象中(做用域鏈中[[scope]]對象裏面)
此時,函數A與A的scope構成closure函數的閉包實例spa
從圖中能夠看到,ar[0](如下稱爲函數A)函數的做用域鏈最頂層爲自身活動對象(arguments, caller, length, name等等構成)再往上則是由一個Closure對象實例構成,能夠看到這一層裏面只包含一個變量i,即建立A時外層函數裏面聲明的變量i。當咱們調用A時,咱們在第二層做用域鏈上面找到的這個i變量。
爲何arr數組每一項都返回的是10,而不是對應的下標值的緣由就在這裏:當咱們調用數組項函數時,遇到變量i,而且在第二層做用域鏈讀取到i,這裏面保存的i就是closure函數裏面定義的i。在A調用時,closure已經執完畢,在closure執行的過程當中i的值從0變到了10。這個性質相似於把本來存在於closure函數中的變量,在closure函數執行完畢後(從內存中移除)咱們能夠在A自身的scope屬性裏訪問到。
簡單一點說就是咱們在調用A函數的時候,訪問到的i變量,是函數closure(雖然它不在了可是它的變量還在,仍然被scope屬性引用。)的當前值(實時值)code
因此要達到預期目標,咱們只須要保證它的scope對象中保存的這個‘i’值是正確的就能夠了。
這裏面的思路就是,函數A被當作普通函數調用時,非閉包狀況下,做用域就是自身(沒有i變量)+ 全局做用域(也沒有),因此這裏仍是須要藉助閉包。即須要保證A上一層做用域的i是正確的值對象
1.建立另一個閉包,每一個i的值都會建立一個閉包ip
function closureCallback(i) { // 返回函數裏面的i變量就是closureCallback函數的參數i return function() { return i } } function closure() { let result = [], b = 'closure' for (var i=0; i<10; i++) { // 這裏的closureCallback做爲普通函數調用 // 且沒有引用closure函數的變量, // 函數做用域內的變量,沒法直接在函數外取得 // 因此做用域鏈不包含closure // 因此在closureCallback函數[[scope]]中不會有closure函數 result[i] = closureCallback(i) } return result }
2.使用匿名閉包內存
function closure() { let result = [], b = 'closure' for (var i=0; i<10; i++) { result[i] = (function (i) { return () => i })(i) } return result } let arr = closure()
3.使用let,減小閉包,let具備塊級做用域的效果
function closure() { let result = [], b = 'closure' for (var i=0; i<10; i++) { let j = i result[i] = function () { return j } } return result } let arr = closure()