Closure

What is closure ?

  • 當一個 function return 另一個 function 時, 外層 function 的封閉環境對內層 function 環境的影響(e.g. variables … etc.)
  • 當外層 function 生命週期結束, 但是內層 function 被保存下來了(e.g. 透過 return 回傳給 global 作用域), 外層 function 的作用域變數之記憶體不會被釋放, 持續供內層 function 使用
1
2
3
4
5
6
7
function greet(whattosay){
return function(name){
console.log(whattosay + ' ' + name);
}
}
var sayHi = greet('Hi');
sayHi('Knuckles'); // 顯示 Hi Knuckles
  • 外層 function : greet(), 內層 function : 被指定為 sayHi()
  • 當 greet(‘Hi’) 執行完時, 由於內層 function 被回傳出來, 因此外層 function(greet()) 的作用域變數不會被釋放
  • 接下來執行 sayHi() 時, 會去該記憶體位址參考值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function buildFunctions(){
var arr = [];
for(var i=0; i<3; i++){
arr.push(function(){
console.log(i);
});
}
return arr;
}

var fs = buildFunctions();

fs[0](); // 都Output : 3
fs[1]();
fs[2]();
  • 概念同上一個例子, 在 main 呼叫fs[0/1/2]() 函數時,由於 Scope 內沒有 ‘i’ 這個變數, 向上一個Scope(生成他的 Scope,也就是 buildFunctions() )去找 ‘i’ , 找到了, 此時的值是 '3', 因為已經跑完迴圈, i 停在3(跳出迴圈), 因此, 不管fs[0/1/2](), 都印出3

  • Solution 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function buildFunctions2(){
var arr = [];
for(var i=0; i<3; i++){
arr.push(
(function(j){ // 標記為 functionA
return function(){ // 標記為 functionB
console.log(j);
}
})(i)
)
}
return arr;
}

var fs2 = buildFunctions2();

fs2[0]();
fs2[1]();
fs2[2]();
  • 整個函數陣列 (arr) 的生成步驟是: 先用 functionA 生成 functionB, 再 push 至 arr, 而且每一次傳入的參數 (j) 由 i 決定, 也就是說每一次生成函數時的 j 值都不一樣了, 各自為獨立的執行環境. 因此呼叫 fs2[0]() 時, 沒有 j, 向上一層找, 找到 j, 且值是 ‘0’; 呼叫 fs2[1]() 時, 沒有 j, 向上一層找, 找到 j, 且值是 ‘1’; 也就是每一次都是獨立的執行環境, 所以 j 的值不同.

  • Solution 2

    • 利用 ES6 - let 作用域的特性
      • 當 let 宣告在 for 迴圈之中時, 每一個 iterations 自成一個作用域
        • 因此每一個 i 在各自 iterations 是個別有自己的記憶體空間的, 互不影響
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          function buildFunctions2(){
          var arr = [];
          for(let i=0; i<3; i++){
          arr.push(function(){ console.log(i)});

          }
          return arr;
          }

          var fs2 = buildFunctions2();

          fs2[0]();
          fs2[1]();
          fs2[2]();

參考文獻