JS的执行顺序一般是从上往下依次进行,比如下面这个函数, 先是声明了 i ,然后利用 for 循环,将 i 不断 +1 ,依次打出。打印结果依次为: 0 1 2 3 4 5
let i = 0
for(i =0; i<6; i++){
console.log(i)
}
如果这时在 for 循环内添加一个延时执行函数,那么结果就会发生变化,将打印出 6个6
let i = 0
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
我们可以在 setTimeout 后面加上一句,以此判断函数的执行顺序。
let i = 0
for(i=0;i<6;i++){
setTimeout(()=>{
console.log(i)
},0)
console.log(i)
}
// 结果为:
0
1
2
3
4
5
6
6
6
6
6
6
当执行到延时函数 setTimeout 时,JS 会先跳过,将 setTimeout 函数放在另一个地方,继续执行 for 循环。打出 0 1 2 3 4 5 ,当for 循环执行完毕之后,再将 setTimeout 函数取出执行,在这个时候 i 经过 for 循环已经变成了 6 ,所以之后 setTimeout 打印出的结果就都是 6 .
以下有几种解决方案,让打印的结果为 0 1 2 3 4 5
可以将 let 声明加入 for 循环的头部,这样会让变量在循环的过程中被声明多次,每次变量变化的时候,值都会再次声明,这样就可以保证在 setTimeout 执行的时候拥有所有 i 的值
for (let i=0; i<6 ; i++){
setTimeout(()=>{
console.log(i)}
,0)
}
除此之外,可以将 setTimeout 放在一个立即执行函数里面,将 i 作为参数传到函数内
let i = 0
for(i=0; i<6; i++){
! function(i){ //加上 ! 是让这个 function 立即执行,否则会报错
setTimeout(()=>{console.log(i)},0)
}(i) //这个 i 是外部的参数 i ,将这个参数 传入到 function 里面去,最后打印出来
}
也可以将 setTimeout 单独放在一个函数里面,等到循环的时候一次次去调用
let a = function(i){
setTimeout(()=>{console.log(i)},0)
}
let i = 0
for(i=0;i<6;i++){
a(i)
}