/

了解javesvript中的var

記錄幾個月前幫助同學遇到的一個問題,同時了解背後的原理。

1
2
3
4
5
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i)
}, 0)
}

output:

5 5 5 5 5

很多人會認為 output 應該為 0 1 2 3 4

要記得 callback function 會被丟到 callback queue 等到 thread 有空才去執行,所以一共有 5 個 callback function 準備要印出 console.log(i)

然而 i 會因為變成 5 而離開迴圈,此時 main thread 已沒有任務可以執行,所以執行 callback queue 中的 5 個 console.log(i),這也是為何 i 的值為 5。

解法

1. let 取代 var

1
2
3
4
5
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i)
}, 0)
}

最間單的解法就是用 let 取代 var,let 會將變數的 scope 鎖定至 block 之中。也就 5 個 console.log 都分別擁有自己的 i 變數,並且生命週期只到迴圈結束。

2. IIFE

1
2
3
4
5
6
7
for (var i = 0; i < 5; i++) {
(function (num) {
setTimeout(() => {
console.log(num)
}, 0)
})(i)
}

let 關鍵字是 es6 的新特性,沒有 es6 的時代可以用 IIFE 來解決。IIFE 為一個立即執行的 function,我們可以把 i 丟到一個新的 function 中去執行,這樣做的原因是因為 function 擁有自己獨立的 scope ,如此一來就可以跟外界的 i 區隔。