JavaScript Function Closure (閉包)

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

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

例如:

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

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

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

const 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
const add5 = makeAdder(5);

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

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

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

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

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

const counter = (function () {
  // 私有變數
  let 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());