【前端工程師手冊】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 = 上次迭代結束的isetTimeout(function() {console.log(i)}, i*1000)
}
所以5個匿名函數引用的并不是同一個i,自然就會順利的間隔一秒分別打印出1、2、3、4、5了