Closure這名詞是函式與作用域環境的組合,作用域環境指的是在存取變數時,決定如何使用哪個變數的環境。
例如我們在函式中宣告一個區域函式如下
function test(){
let b =1;
function g(i){
return i+b;
}
return g;
}
let f = test();
console.log(f(1));
console.log(f(2));
let f = test()執行完之後應該會拿到g函式,可是g函式裡面的b的變數是test裡面所提供的,但我們執行f(1)時,理論上應該只是執行g函式,但我們沒有b變數,在執行i+b應該會發生錯誤才對,但是沒有發生任何問題,程式成功執行,並且拿到1+1=2的結果。
以上的原因是JavaScript會很自然地建立閉包,g函式會用到test裡面的b變數,所以JavaScript會很自然的幫g函式打包保留b變數。
不過事後若b變數的值改變了,那就會改變到我們f(1)的結果。
例如下圖,我們跑回圈將匿名函式指派進farr陣列的元素裡,明明有照順序傳入0、1、2,可是最後再使用一次全部都是拿到3,這是因為被打包進匿名函式的i變數最後被改變為3。
如果希望1是1,2是2,不會因為變數的改變而影響結果的話,在ES6以前,只能使用函式傳值的方式產生新變數。
function g(i){
function gg(){
return i;
}
return gg;
}
而在ES6後,在for迴圈中的var改用let的宣告方式時,每跑一次迴圈執行JavaScript背後會幫let產生新的執行環境偷偷產生新變數,這樣就不會有我們事後執行farr[1]();
結果值不如預期的問題。