關於JS中for迴圈時,作用域問題和this指標指向的總結

常好樂發表於2017-09-19

在大多數計算機語言中,{}這樣一對花括弧叫一個塊級作用域,也就是一個執行環境。在一個執行環境中,執行環境內部的變數在作用域外部式無法被訪問到的。執行環境內部倒是可以訪問外部的變數。
但由於JS中沒有塊級作用域,只有函式作用域。所以類似於像for(;;){ }這樣的作用域,實際上它還是在它的上層作用域之內,如果它上層作用域是global或window,那麼實際上這個for迴圈還是在全域性作用域下。

所以,在典型的JS面試題中出現的例子:

const Greeters = []  
for (var i = 0 ; i < 10 ; i++) {  
  Greeters.push(function () { return console.log(i) })  
}  
Greeters[0]() // 10  
Greeters[1]() // 10  
Greeters[2]() // 10複製程式碼

由於,此處預設for語句是在全域性作用域下,且for迴圈並不會生成塊級作用域,因此,var宣告的i變數也就自然在全域性作用域之下,在for迴圈中又出現了一個匿名函式,這個匿名函式倒是有自己的函式作用域。所以,事實上,這個匿名函式先是寫好放在那裡,還沒有被外部宣告呼叫,它就僅僅地待在那裡,當外部宣告一個Greeters0想要呼叫這個函式作時,由於i是在全域性作用域被var宣告出來的,i已經事先被fior語句迴圈到等於10了,i=10已經事先固定地儲存在全域性作用域之中不動了。程式再才來呼叫的這個函式內部的i。這時,函式只需要去全域性作用域環境去找到那個事先已經被固定下來的i=10的那個i就行了,所以,無論呼叫多少次,它只會去呼叫那個事先已經迴圈完畢,並被放入全域性作用域下i=10那個i。換句話說,for迴圈語句的那個{}相當於可以視為沒有寫。

那麼如何解決這個問題呢?

又因為JS中var來宣告變數時,var宣告出來的變數是在當前作用域環境之下, let宣告出來的變數也是在當前作用域之下。不一樣的是:let可以把類似於這種JS裡面不是塊級作用域的作用域(例如for(;;){})變成一個塊級作用域,而var不會。並且var可以多次宣告同一個變數名,而let不行

var a = 5;   
var a = 3;  
let b = 2;   
let b = 4;  
console.console.log(a);  
console.console.log(b); // Identifier 'b' has already been declared複製程式碼

所以,我們可以用ES6的語法let來建立一個塊級作用域。

const Greeters = []  
for (let i = 0 ; i < 10 ; i++) {  
  Greeters.push(function () { return console.log(i) })  
}  
Greeters[0]() // 0  
Greeters[1]() // 1  
Greeters[2]() // 2複製程式碼

此時,從外部去呼叫由於用let每建立一個i,就會去執行一遍內部的匿名函式,並儲存起來push進陣列Greeters[]裡,當你想要呼叫某一個陣列裡的函式時,就直接Greetersi 呼叫對應的陣列函式並console.log出當前的i了。

相關文章