在常運用閉包是在處理回呼或計時器的時候,需要在某個不確定的時間點、以非同步方式呼叫某函式。
若沒有閉包機制,想要在同一時間執行多個動作,不論是事件處理、動畫、Ajax請求,將會非常困難;閉包不只是把建立時的值拍照紀錄而已,而是在狀態封裝後仍繼續活著,只要閉包存在,就可以修改裡頭的狀態。
function buildFunctions(){
var arr = [];
//for迴圈
for (var i=0 ; i<3 ; i++){
arr.push( //在arr陣列裡放入3個function
function(){
console.log(i);
});//console.log不會在這時被執行,只是創造物件,因為還沒被呼叫
}
return arr;
}
var fs = buildFunctions();
fs[0]();
fs[1]();
fs[2]();
執行結果
for迴圈執行第一次時i為1,第二次i為2,第三次i為3
原因:
因為i++最先執行,最後i為2時,i++又執行了一次i為3,然後判斷i<3的表示式,發現i不小於3時才離開了迴圈,這時i離開的值是3,所以記憶體中i停留的值為3,而不是我們創造函數時候的值,所以三個輸出都一樣是3,因為三個都指向同一個記憶體位置。
console.log不是在它所在的地方執行,而是當我們呼叫函數時執行,i的值是在執行函數時後的值(還沒呼叫)
方法一:
使用ES6的let
function buildFunctions22(){
var arr = [];
//for迴圈
for (var i=0 ; i<3 ; i++){
let j = i; //用let宣告,每次for迴圈執行時,這會是記憶體中一個新的變數,會放在不同的記憶體位置。
arr.push(
function(){
console.log(j);
});
}
return arr;
}
var fs2 = buildFunctions2();
fs2[0]();
fs2[1]();
fs2[2]();
方法二:
IIFE立刻執行函數
獲得執行環境唯一的方法就是執行函數
function buildFunctions22(){
var arr = [];
//for迴圈
for (var i=0 ; i<3 ; i++){
arr.push(
(function(j){
return function(){
console,log(j);
}
}(i))
)
}
return arr;
}
var fs2 = buildFunctions2();
fs2[0]();
fs2[1]();
fs2[2]();
每執行新的一行,每次執行都創造了自己的執行環境,j會被存在這三個執行環境。
j它不需要到迴圈去找,只要到自己的這個執行環境就好,j的值會在迴圈執行時儲存。