我們直接舉栗子說問題。
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000 * i);
}
結果:開始輸出一個 5,然後每隔一秒再輸出一個 5,一共 5 個 5。
因爲setTimeout會在和他同時執行的代碼執行完成之後,再按照時間執行花括號裏面的內容。也就是說,除了console.log(i)這句話,其他都是同時執行的,1000*i也會根據5次循環依次賦值。循環完成之後,再同時開始執行5個setTimeout,這時i已經爲5。所以雖說時間是0秒~4秒,但因爲同一起跑線開始,所以是每隔一秒輸出一個5。
那應該怎麼改才能輸出 0 到 4 呢?
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
添加了一個閉包來記錄i。
那如果刪掉i呢?
for (var i = 0; i < 5; i++) {
(function() {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
結果又會變成5,因爲內部其實沒有對 i 保持引用。
接着變:
for (var i = 0; i < 5; i++) {
setTimeout((function(i) {
console.log(i);
})(i), i * 1000);
}
結果:立馬!輸出 0 到 4(注意是立馬!)
這裏給 setTimeout 傳遞了一個立即執行函數,自調用函數執行完後返回undefine。也就等價於:
setTimeout(undefined, ...);
也就是在立馬輸出0~4後,延時器開始按時間執行,0秒……4秒,但是什麼都沒幹,因爲執行內容爲undefined。
這裏就算把i去掉,一個空傳參的自執行函數,也可以達到同樣的效果
for (var i = 0; i < 5; i++) {
setTimeout((function() {
console.log(i);
})(), i * 1000);
}
或
for (var i = 0; i < 5; i++) {
setTimeout((function() {
console.log(i);
})(i), i * 1000);
}
以上兩種是一樣的,都會一下同時輸出0~4,因爲:
自調用在聲明的時候調用,不是計時器調用的,而聲明是在走循環的時候,也就是循環的時候就執行了。所以傳不傳i唯一的區別就是:用的是局部變量i還是全局變量i,結果沒區別
最後一個!
setTimeout(function() {
console.log(1)
}, 0);
new Promise(function executor(resolve) {
console.log(2);
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve();
}
console.log(3);
}).then(function() {
console.log(4);
});
console.log(5);
結果:2 3 5 4 1
首先先碰到一個 setTimeout,於是會先設置一個定時,在定時結束後將傳遞這個函數放到任務隊列裏面,因此開始肯定不會輸出 1 。
然後是一個 Promise,裏面的函數是直接執行的,因此應該直接輸出 2 3 。
然後,Promise 的 then 應當會放到當前 tick 的最後,但是還是在當前 tick 中。
因此,應當先輸出 5,然後再輸出 4 。
最後在到下一個 tick,就是 1 。
(其實最後一題我不是很懂,只是大概查了一下。歡迎批評指正!)