關於javascript中的閉包

關於javascript中的閉包

我GitHub上的菜鳥倉庫地址: 點擊跳轉查看其餘相關文章
文章在個人博客上的地址: 點擊跳轉

        其實關於閉包的定義,不少種說法,而關於閉包的解釋,更是多不勝數了。不少說得很是複雜,也有不少人有着不一樣的理解,在這裏我就從最容易理解的角度去解釋閉包的概念,若有不正確的地方,請指出。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了。

        關於這個題的詳細理解,你們再細想一下,就很清楚了。