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