天天看點

【前端工程師手冊】JavaScript作用域拾遺

【前端工程師手冊】JavaScript作用域拾遺

昨天總結了一些作用域的知識

【前端工程師手冊】JavaScript之作用域

,但是發表完發現忘記了一些東西,今天拾個遺。

昨天說到了JavaScript中沒有塊級作用域,其實在es6中是有的。

es6中的塊級作用域

先舉個栗子:

var foo = true;
if (foo) {
    let bar = foo * 2;
    bar = something( bar ); 
    console.log(bar);
 }
 console.log( bar );  // ReferenceError           

這個是let最直覺的作用,在一對大括号中建立了塊級作用域,bar會在大括号中的代碼執行完畢後銷毀。

再舉個栗子:

for(var i = 1;i <= 5;i++) {
    setTimeout(function() {
        console.log(i)
    }, i*1000)
}
// 每隔一秒列印一個6,共列印5次           

如果說這段代碼的初衷是間隔1秒列印出1、2、3、4、5的話,結果是令人大跌眼鏡的,真正的結果是每隔1秒列印一次6,列印5次.

為什麼會這樣子?首先是因為閉包的原因,閉包後面再說,現在先了解為閉包是一個函數,一個能夠通路并未在它自己内部定義的變量的函數。

OK,接下來說深層次原因。for循環完畢之後,i=6,且此時生成了5個匿名函數 function(){ console.log(i) },由于這5個匿名函數處在同一個詞法作用域中,是以他們引用同一個i,是以當他們執行時,自然而然就會打出6。

如何解決?

for(let i = 1;i <= 5;i++) {
    setTimeout(function() {
        console.log(i)
    }, i*1000)
}
// 間隔一秒分别列印出1、2、3、4、5           

把var換成let聲明就可以了。

《你不知道的JavaScript-上卷》中解釋道:

for 循環頭部的 let 不僅将 i 綁定到了 for 循環的塊中,事實上它将其重新綁定到了循環的每一個疊代中,確定使用上一個循環疊代結束時的值重新進行指派。

說白了就是再每次疊代内部,都會對 i 進行隐形的重新指派,且使用的是上一個疊代結束時的值來對 i 進行重新指派。

差不多就是這樣的:

for(let i = 1;i <= 5;i++) {
    let i = 上次疊代結束的i
    setTimeout(function() {
        console.log(i)
    }, i*1000)
}           

是以5個匿名函數引用的并不是同一個i,自然就會順利的間隔一秒分别列印出1、2、3、4、5了

繼續閱讀