深入理解javascript系列(八):閉包(2)

Panthon發表於2018-06-13

本次筆記主要記錄了:

閉包與垃圾回收機制的關係,

閉包與作用域鏈的關係。

8.1  閉包與垃圾回收機制

在系列(二)中,我提到了垃圾回收機制,知道當一個值失去引用之後就會被標記,然後被垃圾回收機制回收並釋放空間。

我們知道,當一個函式的執行上下文執行完畢之後,內部所有內容都會失去引用而被垃圾回收機制回收。

我們還知道,閉包的本質就是在函式外部保持了內部變數的引用,因此很明顯閉包會阻止垃圾回收機制回收。

下面我們就用一個例子來證明這一點。

function f1() {
    var n = 999;
    nAdd = function() {
        n += 1;
    }

    return function f2() {
        consloe.log(n);
    }
}
var result = f1();
result();  //999
nAdd();
result();  //1000複製程式碼

從上面的例子可以看出,因為nAdd、f2都訪問了f1中的n,因此它們都與f1形成了閉包。這個時候變數n的引用被保留了下來。因為f2與nAdd執行時都訪問了n,而nAdd每執行一次就會將n加1.

認識到閉包中儲存的內容不會被釋放之後,我們就應該謹慎使用閉包。因為誰都知道,閉包的濫用可能會導致記憶體洩露因而影響程式效能。

8.2  閉包與作用域鏈

在開始之前,先思考一個問題,閉包會導致函式的作用域鏈發生改變嗎?

我們結合下面的例子來分析一下。

var fn = null;
function foo() {
    var a = 2;
    function innerFoo() {
        console.log(a);
    }
    fn = innerFoo;    // 將innerFoo的引用賦值給全域性變數中的fn
}
function bar() {
    fn();  //此處保留innerFoo的引用
}
foo();
bar(); //2複製程式碼

在這個例子中,foo內部的innerFoo訪問了foo的變數a。因此當innerFoo執行時會有閉包產生,這是一個比較簡單的閉包的例子。不一樣的地方在與全域性變數fn。fn在foo內部獲取了innerFoo的引用,並在bar中執行。

那麼innerFoo的作用域鏈會是怎麼樣的呢?

深入理解javascript系列(八):閉包(2)

在這裡需要特別注意的地方是函式呼叫棧與作用域鏈的區別:

因為call stack其實是在程式碼執行時才確定的,而作用域的規則在程式碼編譯階段就已經確定了。雖然作用域鏈的在程式碼執行時才生成的,但是他的規則不會隨著程式碼執行而發生變化。

so,這裡的閉包的存在並不會導致作用域鏈發生變化。

這些都是我以往的學習筆記。如果您看到此筆記,希望您能指出我的錯誤。有這麼一個群,裡面的小夥伴互相監督,堅持每天輸出自己的學習心得,不輸出就出局。希望您能加入,我們一起終身學習。歡迎新增我的個人微訊號:Pan1005919589


相關文章