JS的执行顺序一般是从上往下依次进行,比如下面这个函数, 先是声明了 i ,然后利用 for 循环,将 i 不断 +1 ,依次打出。打印结果依次为: 0 1 2 3 4 5

    1. let i = 0
    2. for(i =0; i<6; i++){
    3. console.log(i)
    4. }

    如果这时在 for 循环内添加一个延时执行函数,那么结果就会发生变化,将打印出 6个6

    1. let i = 0
    2. for(i = 0; i<6; i++){
    3. setTimeout(()=>{
    4. console.log(i)
    5. },0)
    6. }

    我们可以在 setTimeout 后面加上一句,以此判断函数的执行顺序。

    1. let i = 0
    2. for(i=0;i<6;i++){
    3. setTimeout(()=>{
    4. console.log(i)
    5. },0)
    6. console.log(i)
    7. }
    8. // 结果为:
    9. 0
    10. 1
    11. 2
    12. 3
    13. 4
    14. 5
    15. 6
    16. 6
    17. 6
    18. 6
    19. 6
    20. 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 的值

    1. for (let i=0; i<6 ; i++){
    2. setTimeout(()=>{
    3. console.log(i)}
    4. ,0)
    5. }

    除此之外,可以将 setTimeout 放在一个立即执行函数里面,将 i 作为参数传到函数内

    1. let i = 0
    2. for(i=0; i<6; i++){
    3. ! function(i){ //加上 ! 是让这个 function 立即执行,否则会报错
    4. setTimeout(()=>{console.log(i)},0)
    5. }(i) //这个 i 是外部的参数 i ,将这个参数 传入到 function 里面去,最后打印出来
    6. }

    也可以将 setTimeout 单独放在一个函数里面,等到循环的时候一次次去调用

    1. let a = function(i){
    2. setTimeout(()=>{console.log(i)},0)
    3. }
    4. let i = 0
    5. for(i=0;i<6;i++){
    6. a(i)
    7. }