for循环与setTimeout

// 修改下面代码,使其可以正确运行
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。babel

而后是和做用域的关系,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的引用就能获得正确的值。spa

clipboard.png

相关文章
相关标签/搜索