JavaScript Function Closure (閉包)

Closure (中文稱做閉包) 是一個屬於函數特殊的執行環境 (scope; context),這「封閉」的環境中保存 (record) 了讓函數可以持續 (甚至在 function 被 return 後) 存取的獨立自由變數 (free variable),換句話說,closure 是讓函數能「記得」被建立時的環境的一種機制。

那怎麼建立一個 closure?很簡單,從一個 function return 另一個 nested function 就可以建立一個 closure 環境

例如:

function makeFunc() {
    // 一個局部變數
    var name = 'Fooish';

    function displayName() {
        // 內部函數可以存取外部函數的變數
        alert(name);
    }

    // 返回一個內部函數,並創建一個 closure  
    return displayName;
}

var myFunc = makeFunc();

// 顯示 Fooish
myFunc();

上面例子中,makeFunc() 執行時返回一個 function,同時自動創建了一個 closure 環境。closure 像是一個特殊的物件 (指定給了 myFunc),closure 中包含一個函數 (displayName),以及函數 (makeFunc) 執行當時的環境,讓你在函數返回後還是可以持續存取 closure 保存的環境 (像是能存取 name 變數,name 變數不會因為函數返回後而被刪除)。

而每一個 closure 中保存的都是一個獨立的環境。

再舉一個 closure 的例子:

function makeAdder(x) {

    return function(y) {
        // 函數被 return 後,還是能繼續存取環境中的 x 變數
        return x + y;
    };
}

// 建立一個 closure
var add5 = makeAdder(5);

// 建立另一個 closure
var add10 = makeAdder(10);

// 顯示 7
alert(add5(2));

// 顯示 12
// 兩個 closure 的環境是獨立的
alert(add10(2));

其實不僅是 return function 才能建立一個 closure,你也可以返回任何 object,只要這 object 會持續存取內部環境的變數。

舉個利用 IIFE 和 closure 實作私有方法 (private method) 的例子:

var counter = (function() {

    // 私有變數
    var privateCounter = 0;

    // 私有方法
    function changeBy(val) {
        // 存取 closure 環境中的變數
        privateCounter += val;
    }

    // 返回一個 object,並建立一個 closure 環境  
    return {

        increment: function() {
            // 存取 closure 環境中的函數
            changeBy(1);
        },

        decrement: function() {
            // 存取 closure 環境中的函數
            changeBy(-1);
        },

        value: function() {
            // 存取 closure 環境中的變數
            return privateCounter;
        }
    };
}());

// 顯示 0
alert(counter.value());

counter.increment();
counter.increment();

// 顯示 2
alert(counter.value());

counter.decrement();

// 顯示 1
alert(counter.value());