JavaScript 的閉包

一顆白菜發表於2018-04-25

1. 閉包的概念

據我們所知,區域性變數在函式退出之後就不佔據記憶體空間,但存在一種特殊的函式,能使區域性變數在函式退出之後繼續佔據記憶體,為外部函式所呼叫,這個特殊的函式就是閉包。那麼閉包是怎麼做到將區域性函式一直佔據記憶體的呢?看看下面的例子。

function a(){
    var Aitem=2;//定義區域性變數item
    function b() { //定義內部函式b()
        alert(Aitem);
    }
    b();
}
a();//結果是2
複製程式碼

2. 閉包的機理

從上可以看出內部函式可以讀取外部函式的區域性變數。此時的內部函式的作用域僅限於外部函式a(),只有通過呼叫外部函式a()才可以執行內部函式b()。當外部函式執行結束,內部函式的記憶體也將清除。但是如果我們將內部函式賦給一個全域性變數,那麼內部函式將從區域性變數轉化成全域性變數。即使外部函式退出了,作為全域性變數的內部函式也將儲存在記憶體中。那麼如何讓內部函式成為全域性變數呢?

function a(x){
    var Aitem=2;//定義區域性變數item
    return function b(y) { //a函式結果返回一個b函式
        alert(x+y+(++Aitem));
    }
}
var closure=a(1);//將a(1)return的結果b()賦給全域性變數,closure=b(y){ alert(1+y+(++Aitem))}
closure(1);//彈出5;closure(1)=b(y){ alert(1+1+(++2))}
closure(1);//彈出6;closure(1)=b(y){ alert(1+1+(++3))}
複製程式碼

上例中的closure()函式即閉包。第一次closure(1)中Aitem=2,第二次closure(1)中Aitem=3,可以看出item這個區域性變數在a()退出以後並沒有被清除,而是像一個全域性變數一樣一直儲存在記憶體中。這就是閉包的神奇所在。closure()閉包依賴於b(y),而b(y)又是a(x)的區域性函式,故closure()閉包可以實時訪問a(x)的區域性變數。如果沒有將內部函式裝化成全域性變數的話,就形不成閉包,請看下面的例子:

function a(x){
    var Aitem=2;//定義區域性變數item
    function b(y) { //定義區域性函式b
        alert(x+y+(++Aitem));
    }
     b(1);//在a()內部執行b(1);即alert(x+1+(++2))
}
a(1);//彈出5;即alert(1+1+(++2))
複製程式碼

上例沒有將區域性函式b()賦給全域性變數,在a(1)執行以後b()就不復存在,Aitem也無從獲取。


3. 總結

閉包是可以讀取其他函式內部變數的函式,是外部函式和內部函式的一座橋樑。

閉包擁有兩個作用:

  • 使區域性變數keep alive
  • 讀取其他函式的區域性變數

閉包的缺點:

  • 常駐記憶體,造成記憶體的浪費

相關文章