昨天總結了一些作用域的知識
【前端工程師手冊】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了