關閉

宣告函式時,其宣告的上下文中的變數將在其範圍內捕獲。例如,在下面的程式碼中,變數 x 繫結到外部作用域中的值,然後在 bar 的上下文中捕獲對 x 的引用:

var x = 4; // declaration in outer scope

function `bar()` {
    console.log(x); // outer scope is captured on declaration
}

bar(); // prints 4 to console

樣品輸出:4

這種捕獲範圍的概念很有意思,因為即使在外部範圍退出之後,我們也可以使用和修改外部範圍中的變數。例如,請考慮以下事項:

function `foo()` {
    var x = 4; // declaration in outer scope

    function `bar()` {
        console.log(x); // outer scope is captured on declaration
    }

    return bar;
    
    // x goes out of scope after foo returns
}

var barWithX = foo();
barWithX(); // we can still access x

樣品輸出:4

在上面的例子中,當呼叫 foo 時,它的上下文被捕獲在函式 bar 中。所以即使它返回後,bar 仍然可以訪問和修改變數 x。函式 foo,其上下文在另一個函式中捕獲,被稱為閉包

私人資料

這讓我們做了一些有趣的事情,比如定義僅對特定函式或函式集可見的私有變數。一個人為的(但很受歡迎的)例子:

function `makeCounter()` {
    var counter = 0;

    return {
        value: function () {
            return counter;
        },
        increment: function () {
            counter++;
        }
    };
}

var a = makeCounter();
var b = makeCounter();

a.increment();

console.log(`a.value()`);
console.log(`b.value()`);

樣本輸出:

1
0

呼叫 makeCounter() 時,將儲存該函式上下文的快照。makeCounter() 中的所有程式碼都將在執行時使用該快照。因此,makeCounter() 的兩個呼叫將建立兩個不同的快照,其中包含自己的 counter 副本。

立即呼叫的函式表示式(IIFE)

閉包還通常通過使用立即呼叫的函式表示式來防止全域性名稱空間汙染。

立即呼叫的函式表示式 (或者,更直觀地說,是自執行的匿名函式 )本質上是在宣告之後立即呼叫的閉包。IIFE 的一般想法是呼叫建立單獨上下文的副作用,該上下文只能由 IIFE 中的程式碼訪問。

假設我們希望能夠用 $ 引用 jQuery。考慮一下天真的方法,不使用 IIFE:

var $ = jQuery;
// we've just polluted the global namespace by assigning window.$ to jQuery

在下面的示例中,IIFE 用於確保 $ 僅在閉包建立的上下文中繫結到 jQuery

(function ($) {
    // $ is assigned to jQuery here
})(jQuery);
// but window.$ binding doesn't exist, so no pollution

有關閉包的更多資訊,請參閱 Stackoverflow 上的規範答案