在大多數計算機語言中,{}這樣一對花括弧叫一個塊級做用域,也就是一個執行環境。在一個執行環境中,執行環境內部的變量在做用域外部式沒法被訪問到的。執行環境內部卻是能夠訪問外部的變量。
但因爲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循環語句的那個{}至關於能夠視爲沒有寫。bash
那麼如何解決這個問題呢?函數
又由於JS中var來聲明變量時,var聲明出來的變量是在當前做用域環境之下, let聲明出來的變量也是在當前做用域之下。不同的是:let能夠把相似於這種JS裏面不是塊級做用域的做用域(例如for(;;){})變成一個塊級做用域,而var不會。而且var能夠屢次聲明同一個變量名,而let不行ui
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來建立一個塊級做用域。 spa
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了。code