前言
在我以前寫過的兩篇文章:我知道你懂 hoisting,可是你了解到多深?以及所有的函式都是閉包:談 JS 中的作用域與 Closure 裡面,都談到了 let 與 var 作用域的不同。
let 的作用域是 block,而 var 則是 fucntion,像這個就是經典案例:
for(var i=1; i<=10; i++) {
setTimeout(function() {
console.log(i)
})
}
原本預期會依序輸出 1~10,沒想到卻輸出了 10 個 11。背後原因就是因為第三行那個 i 永遠都只有一個,就是 for 迴圈宣告的那個 var i
,從頭到尾都是同一個變數。
而經典解法也很簡單,把 var 改成 let 就搞定了:
for(let i=1; i<=10; i++) {
setTimeout(function() {
console.log(i)
})
}
搞定的原因是,可以把上面程式碼看作是下面這種形式:
{
let i=1
setTimeout(function() {
console.log(i)
})
}
{
let i=2
setTimeout(function() {
console.log(i)
})
}
...
{
let i=10
setTimeout(function() {
console.log(i)
})
}
由於 let 的作用域是 block,所以在每一圈迴圈裡面,其實都是一個新的 i,因此迴圈跑 10 圈就有了 10 個不同的 i,最後當然是輸出 10 個不同的數字。
因此 var 跟 let 在這個範例最大的區別就在於變數的數量,前者只有 1 個,後者卻有了 10 個。
好,既然知道了 let 與 var 的差別以後,就可以來看看這篇最主要想討論的問題。
其實這問題是來自於 YDKJS(You Dont Know JS,中譯本翻做:你不知道的JavaScript) 的作者 @getify 在他的推特上所提出的:
question for JS engines devs…
is there an optimization in place for this kind of code?
for (let i = 0; i < 10; i++) {
}
IOW, where the behavior of creating a new i
per iteration is not needed nor observable… does JS skip doing it?
若是沒有看得很懂,可以繼續看延伸的另外一則推:
here’s a variation on the question… will JS engines exhibit much performance difference between these two loops?
for (var i = 0; i < 100000000; i++) {
}
for (let i = 0; i < 100000000; i++) {
}
簡單來說呢,平常用 let 搭配迴圈的時候,不是如我們上面所說的,每一圈都會有一個新的i
嗎?既然是這樣的話,那 var 與 let 應該就會有效能上的差異,因為 let 必須每一圈都 new 一個新的變數出來,所以 let 會比較慢。
那如果迴圈裡面並不需要每一圈都有新的 i,JS 引擎會做優化嗎?這個問題就是這樣,主要是想探討 JS 引擎會不會針對這種行為去做優化。
那要怎麼知道呢?要嘛你是 JS 引擎的開發者,要嘛你去看 JS 引擎的原始碼,但這兩種難度都有點太高。不過別擔心,還有第三種:看 JS bytecode。
閱讀更多