一道经典的面试题,也是在日常开发中经常会遇到的问题。
如下代码:
for(var i=0; i<5; i++) {
setTimeout(function() {
console.log(i)
},1000)
}
执行后发现他神奇的输出了5,5,5,5,5
。而不是0,1,2,3,4
。
原因是因为for循环不会等待异步任务执行结果。原理:Js是单线程的,这就意味着所有任务需要放入任务队列,一件事干完才能下一件事。异步任务会放在执行栈的尾部,当所有同步任务执行完,才会执行异步任务。所以当for循环结束,最后一次i++的时候变量i已经是5了,然后再依次执行5次这个setTimeout
里的函数体,因此打印出了5,5,5,5,5
解决方法:
方法1:var 改为 let,因为let 声明的变量只在 let 命令所在的代码块内有效。
for(let i=0; i<5; i++) {
setTimeout(function() {
console.log(i)
},1000)
}
//输出0,1,2,3,4
方法2:使用try...catch,因为catch花括号内是一个块级作用域,可以保存循环变量,产生类似闭包效果。
for(var i=0; i<5; i++) {
try{
throw(i)
} catch(j) {
setTimeout(function() {
console.log(j)
},1000)
}
}
//输出0,1,2,3,4
方法3:使用自执行函数,产生闭包作用域。
for(var i=0; i<5; i++) {
(function(i) {
setTimeout(function() {
console.log(i)
},1000)
})(i)
}
//输出0,1,2,3,4