for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i)
},1000)
}
會打印出5個6,這是why
因為 for 循環會先執行完(同步優先於異步優先於回調),這時五個 setTimeout 的回調全部塞入了事件隊列中,然后 1 秒后一起執行了。
為什么不是 1 2 3 4 5 ,問題出在作用域上。
為 setTimeout 的 console.log(i); 的i是 var 定義的,所以是函數級的作用域,不屬於 for 循環體,屬於 global。
等到 for 循環結束,i 已經等於 5 了,這個時候再執行 setTimeout 的五個回調函數(參考上面對事件機制的闡述),里面的 console.log(i);
的 i 去向上找作用域,只能找到 global下 的 i,即 5。所以輸出都是 5。
辦法1:人為給 console.log(i); 創造作用域,保存i的值。for
(
var
i = 0; i < 5; i++) {
(
function
(i){
//立刻執行函數
setTimeout(
function
(){
console.log(i);
},1000);
})(i);
}
這里用到立刻執行函數。這樣 console.log(i); 中的i就保存在每一次循環生成的立刻執行函數中的作用域里了。
辦法2
for
(let i = 0; i < 5; i++) {
//let 代替 var
setTimeout(
function
(){
console.log(i);
},1000);
}
let 為代碼塊的作用域,所以每一次 for 循環,console.log(i); 都引用到 for 代碼塊作用域下的i,因為這樣被引用,所以 for 循環結束后,這些作用域在 setTimeout 未執行前都不會被釋放。