一道經(jīng)典的面試題唠亚,也是在日常開發(fā)中經(jīng)常會遇到的問題链方。
如下代碼:
for(var i=0; i<5; i++) {
setTimeout(function() {
console.log(i)
},1000)
}
執(zhí)行后發(fā)現(xiàn)他神奇的輸出了5,5,5,5,5
持痰。而不是0,1,2,3,4
。
原因是因為for循環(huán)不會等待異步任務(wù)執(zhí)行結(jié)果祟蚀。原理:Js是單線程的工窍,這就意味著所有任務(wù)需要放入任務(wù)隊列割卖,一件事干完才能下一件事。異步任務(wù)會放在執(zhí)行棧的尾部患雏,當(dāng)所有同步任務(wù)執(zhí)行完鹏溯,才會執(zhí)行異步任務(wù)。所以當(dāng)for循環(huán)結(jié)束淹仑,最后一次i++的時候變量i已經(jīng)是5了丙挽,然后再依次執(zhí)行5次這個setTimeout
里的函數(shù)體,因此打印出了5,5,5,5,5
解決方法:
方法1:var 改為 let匀借,因為let 聲明的變量只在 let 命令所在的代碼塊內(nèi)有效颜阐。
for(let i=0; i<5; i++) {
setTimeout(function() {
console.log(i)
},1000)
}
//輸出0,1,2,3,4
方法2:使用try...catch,因為catch花括號內(nèi)是一個塊級作用域吓肋,可以保存循環(huán)變量凳怨,產(chǎn)生類似閉包效果。
for(var i=0; i<5; i++) {
try{
throw(i)
} catch(j) {
setTimeout(function() {
console.log(j)
},1000)
}
}
//輸出0,1,2,3,4
方法3:使用自執(zhí)行函數(shù)是鬼,產(chǎn)生閉包作用域肤舞。
for(var i=0; i<5; i++) {
(function(i) {
setTimeout(function() {
console.log(i)
},1000)
})(i)
}
//輸出0,1,2,3,4