// 修改下面代码,使其能够正确运行
for(var i = 0; i < 6; i++) {
setTimeout(function timer(){
console.log(i)
}, i * 1000)
}
为什么上面代码的执行结果是 [6,6,6,6,6,6] ?
首先说明,这与异步和作用域都有关系。
先来说和异步的关系,setTimeout是异步操作,所谓的异步就是会在同步操作全部执行完成之后,才开始执行的。放在上面的代码中解释就是,setTimeout是在每次for循环的时候都会调用的(用于将setTimeout内部的代码放入队列,在同步代码执行完成之后,再根据定时执行),但是setTimeout中的代码是在for循环结束之后才开始执行的,所以当for循环执行完成的时候,i变成了6,那么timer中对i的引用也变成了6。
然后是和作用域的关系,for循环中定义的变量i ,它的作用域是什么?
for(var i = 0; i<6; i++) ----> var i = 0; for(i; i<6; i++)
是 window,所以当for循环执行完成的时候,全局变量i的值为6,此时去执行队列中的timer函数,但是timer中并没有定义i,所以就会沿着作用域链向外层查找,就找到了window中的全局变量i,值为6。
// 第一种方法:使用立即执行函
for(var i = 0; i < 6; i++) {
(function(j){
setTimeout(function timer(){
console.log(j)
}, j * 1000)
})(i)
}
// 第二种方法:使用let关键字
for(let i = 0; i < 6; i++) {
setTimeout(function timer(){
console.log(i)
}, i * 1000)
}
这两种方法的本质其实是一样的,都是创建了一个新的变量去保存for循环中每次变化的i的值,再将其传递给timer,使timer每次在执行的时候都能得到正确的值。在使用let的时候,可以看到babel将其转译之后的结果,使用了一个新的参数来保存每次循环时的i,所以在6次循环中,会开辟出6个内存空间,保存着6个不同的i ,这样的话,setTimeout中对i的引用就能得到正确的值。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。