接下來就來一一對於前面三點做個深入的理解
當函式的變數在 自己的範疇(scope)中找不到符合的值時,會往外層範疇(scope)尋找,直到找到全域後才停止,而這也稱為範疇鍊(scope chain)。
來看看這個測試例子了解一下範疇鍊(scope chain):
var text = "This is a text";
function getText(){
console.log(text);
}
getText();
getText
函式中因為沒有變數 text
,所以往外層的全域尋 text
變數並使用它的值 This is a text
。
直接透過一個測試例子來看看:
function outerScope(){
var scope = 'This is outer scope value';
return function(){
return scope;
}
}
var getText = outerScope();
console.log(getText);
console.log(getText());
來看看程式碼執行的流程:
outerScope
函式 、 getText
變數 存入記憶體中,等待被執行。getText
變數的值時, outerScope
函式被執行,建立 outerScope
函式的執行環境,回傳內部的函式,到這裡所獲得的是 console.log(getText);
的結果。outerScope
函式執行完成後會被JavaScript清除,但是 scope
變數會被保留,因為最內層的函式需要在被執行時取得 scope
變數的值。
getText
的值是一個函式,所以當執行這個函式時,裡面的 scope
變數在自己的範疇(scope)找不到這個變數,所以往外層 outerScope
函式中尋找並得到值 This is outer scope value
,這裡是console.log(getText());
的結果。而這就是一個完整閉包的運作流程。
function test(){
var arr =[];
for(var i = 0; i < 3; i++){
arr.push(function(){
console.log(i);
})
}
return arr;
}
var result = test();
result[0]();
result[1]();
result[2]();
此時 result
的值為一組儲存了三個函式的元素,當依序取出函式中的 i
的值時,預期要得到的值分別為 0
、1
、2
。
但透過下圖可以發現結果卻不如預期:
來試著了解程式執行的操作流程:
test
函式 、 result
變數 存入記憶體中,等待被執行。result
變數的值時, test
函式被執行,建立 test
函式的執行環境,for
迴圈會依序將函式作為元素值存入陣列中,這邊要注意的是作為陣列元素存入的函式還沒有被執行,所以並不會得到值為 0
、1
、2
。
result[0]()
、result[1]()
、result[2]()
時,因為閉包(closure)的觀念,內部函式會取得 for
迴圈的 i
值,此時的 i
值則是已經跑完 for
迴圈後的值,而且因為處於相同的範疇(scope),所以所有的執行結果最後都會拿到值為 3
因為 JS 在ES6之前是 函式範疇(function scope),所以上述的程式碼在每次執行時,i
都是處於同一個範疇(scope)中,進而得到上述的結論。
那要怎麼樣才能得到預期的結果,輸出為 0
、1
、2
呢?
剛剛有提到因為處於同一個範疇(scope)中而導致不是預期的結果,所以是不是 讓每次的 i
值都處於不同的範疇(scope)中就可以解決了?
let
、 const
變數首先先來看看透過 IIFE 立即函式改寫後的測試例子:
function test(){
var arr =[];
for(var i = 0; i < 3; i++){
arr.push((function(){
console.log(i);
})());
}
return arr;
}
var result = test();
IIFE 會建立一個新的範疇(scope),所以在每次的 for
迴圈都建立一個新的範疇(scope)就能得到預期的值
再來看看透過 ES6 的 let
、 const
變數:
function test(){
var arr =[];
for(let i = 0; i < 3; i++){
arr.push(function(){
console.log(i);
});
}
return arr;
}
var result = test();
result[0]();
result[1]();
result[2]();
因為 let
、 const
變數會建立 區塊範疇(block scope),所以在每次在執行 for
迴圈時都是處於不同的範疇(scope),所以也能達到預期的結果。
以上就是閉包的一些觀念,今天就先到這裡囉~
明天見~