其實關於閉包的定義,不少種說法,而關於閉包的解釋,更是多不勝數了。不少說得很是複雜,也有不少人有着不一樣的理解,在這裏我就從最容易理解的角度去解釋閉包的概念,若有不正確的地方,請指出。javascript
閉包,其實就是可以讀取函數內部變量的函數。java
舉個例子:git
//全局環境 function outer(){ var big = 20; } console.log(big);
固然了,執行上面這樣的代碼會報錯,由於你沒有在全局環境中聲明big,你想訪問outer函數內部的big也是不行的,由於你沒有調用outer函數,壓根不會建立outer的執行上下文,更不用說outer裏面的big的做用域只是在outer函數內部了。github
那如何可以在外部訪問到outer函數裏面的big變量呢?有辦法,就是閉包:面試
//全局環境 function outer(){ var big = 20; function inner(){ console.log(big); }; return inner(); } outer();//20
你執行outer函數的時候,裏面返回的是調用inner函數的結果,而inner函數就是訪問big變量的,因此這樣就能使外部能夠訪問到函數裏面的變量。這就是所謂的閉包,沒有傳說中的那麼複雜,不過網上的所謂閉包面試題就解釋得很複雜了。閉包
可是閉包有一個問題,就是會使得你內部訪問的變量常駐內存當中,垃圾機制又沒有將其回收,若是此函數再也不使用了,又沒有對其進行清除,就會形成內存泄漏,若是過多使用閉包,後果可想而知。函數
其實換一個角度,咱們看這樣的例子:code
//全局環境 var bigger = 30; function outer(){ console.log(bigger); } outer();//30
像上面的例子,全局環境下聲明的outer函數,裏面調用了全局環境的bigger變量,不又是一個閉包嗎?其實沒錯,這是廣義的閉包,不是常說的閉包,可是轉頭想一想,其實這個bigger是一直存在於全局的執行上下文中的全局變量,你不清除掉不也同樣一直存在內存中嗎?對象
一句話理解閉包類題目:若是a函數內的其餘b函數用到了a函數執行上下文中的變量n,那麼這個變量n的值就會一直保存在這個函數的變量對象當中,直到下一次改變它。ip
舉個網上的面試例子來講明我這個理解:
//全局環境 function fun(n,k) { console.log(k) return { fun:function(m){ return fun(m,n); } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3); var b = fun(0).fun(1).fun(2).fun(3); var c = fun(0).fun(1); c.fun(2); c.fun(3);
這個例子也是隨便一搜的,其餘例子其實也類同,因此在這裏就用我我的理解的角度去分析一下這個題。
先說var a 這一行的輸出吧:
var a = fun(0)很容易知道輸出undefined了,可是返回的是一個對象,這個對象的fun屬性是一個匿名函數,而這個匿名函數裏面又返回了一個fun函數,這個fun函數用到了變量n。好了,用上我前面的理解,能夠知道fun函數裏面就保存了n=0這個值,直到下一次改變它。相似以下:
var a = { fun:function(m){ return fun(m,0); } };
而從a.fun(1)、a.fun(2)、 a.fun(3)開始,你們確定知道會執行這三個函數:
f(1,0);f(2,0);f(3,0);
因此會輸出undefined 0 0 0。
可能你們會懵了,你在執行f(1,0)的時候,不是n的值改成1,不是會改變了n的值了嗎?後面的n的值不是變了嗎?在這裏我就要說說關於個人理解中,怎樣爲改變這個變量的值。
先再回顧一下我前面的理解:若是a函數內的其餘b函數用到了a函數執行上下文中的變量n,那麼這個變量n的值就會一直保存在這個函數的變量對象當中,直到下一次改變它。
改變這個變量的n值,必須經過閉包,也就是這個:
{ fun:function(m){ return fun(m,n); } };
經過這個fun屬性對應的匿名函數的執行,纔會在匿名函數的返回結果中改變這個n的值。
在上面的執行a.fun(1)中,只是又返回了一個新的對象,可是並無執行新對象裏面的fun屬性對應的匿名函數喔,那就是沒有改變n的值啊,因此你繼續a.fun(2)、a.fun(3)也會輸出一樣的結果啊。
也就是,必須有執行這樣:a.fun(1).fun(1)這樣,纔會改變到n的值。
對於上面例題var b這一行來講,就很容易明白var b = fun(0).fun(1).fun(2).fun(3);後面的fun(1).fun(2).fun(3)這三個中的fun其實都是屬性名fun,並非聲明的函數fun,因此他們都在改變n的值啊,因此也很容易明白這一行的輸出結果就是undefined 0 1 2。
var c的那一行也很容易明白輸出結果是undefined 0 1 1了。
關於這個題的詳細理解,你們再細想一下,就很清楚了。