解决循环输出问题

案例:

1
2
3
4
5
for(var i = 1; i <= 5; i ++){
setTimeout(function() {
console.log(i)
}, 0)
} // 输出结果:6 6 6 6 6

如何实现打印结果为 1,2,3,4,5 呢?
1、setTimeout 为宏任务,由于 JS 中单线程 eventLoop 机制,在主线程同步任务执行完后才去执行宏任务,因此循环结束后 setTimeout 中的回调才依次执行
2、因为 setTimeout 函数也是一种闭包,往上找它的父级作用域链就是 window,变量 i 为 window 上的全局变量,开始执行 setTimeout 之前变量 i 已经就是 6 了,因此最后输出的连续就都是 6。

解决方法一:IIFE 立即执行函数
利用 IIFE(立即执行函数),当每次 for 循环时,把此时的变量 i 传递到定时器中,然后执行。

1
2
3
4
5
6
7
for(var i = 1;i <= 5;i++){
(function(j){
setTimeout(function timer(){
console.log(j)
}, 0)
})(i)
} // 输出结果:1 2 3 4 5

解决方法二:使用 ES6 中的 let
ES6 中新增的 let 定义变量的方式,使得 ES6 之后 JS 发生革命性的变化,让 JS 有了块级作用域,代码的作用域以块级为单位进行执行。

1
2
3
4
5
for(let i = 1; i <= 5; i++){
setTimeout(function() {
console.log(i);
},0)
} // 输出结果:1 2 3 4 5

解决方法三:定时器传入第三个参数
setTimeout 作为经常使用的定时器,它是存在第三个参数的,日常工作中我们经常使用的一般是前两个,一个是回调函数,另外一个是时间,而第三个参数用得比较少。而第三个参数的传递,可以改变 setTimeout 的执行逻辑,从而实现我们想要的结果

1
2
3
4
5
for(var i=1;i<=5;i++){
setTimeout(function(j) {
console.log(j)
}, 0, i)
} // 输出结果:1 2 3 4 5