JS 函数的执行时机(for,let和setTimeout()的结合使用)

91 阅读2分钟

JS 函数的执行时机(for,let和setTimeout()的结合使用)

我们先来看下面一段代码A:

let i = 0
for(i = 0; i<6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}
--------------输出结果----------------------------------------------------------------------
->   6
->   6
->   6
->   6
->   6
->   6

我们可以看到,输出结果是6个6。有些人可能会问,为什么不是0,1,2,3,4,5?
原因就在于:函数里面的setTimeout。不知道这是什么的,可以看看这篇文章,关于setTimeout()你所不知道的地方,详解setTimeout()
这里的setTimeout定时器会在循环结束后再执行里面的代码,循环结束后,i变为了6,而setTimeout被循环调用了6次,所以这时输出6个6。

接下来和let有关:

代码B:

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

我们可以看到这次的输出结果是0,1,2,3,4,5
而代码改变的地方就只有一开始的i的声明定义放到了for循环里。为什么会变成这样了?可能你也看很出来了,原因在于let关键字
首先,我们要知道let是什么?ES6简介 let 和 const 命令,该连接里面有let的详细介绍和用法。

接下来说原因:
在代码段B中,变量i是用let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以每一轮的输出结果是不一样的,分别是0,1,2,3,4,5。
你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值?这是因为 JS 引擎会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的结果上进行计算。所以就算是for和setTimeout()执行的顺序不变,但是打印输出的还是本次循环i的值。

不是有let,有没有其他办法也输出0,1,2,3,4,5?

当然是有的。

第一种:利用 setTimeout 的第三个参数

for(i = 0; i<6; i++){
  setTimeout((i)=>{
    console.log(i)
  },0,i)
}

第二种:利用闭包

for(var i=0; i<6; i++) {
    !(function(j) {
	setTimeout(function(){
            console.log(j)
                }, 0)
    })(i)
}

我的解释(可能不对):使用闭包,匿名函数里调用的i是外部的,只作用于本次循环,在外部的i因为循环继续增加,所以输出结果是0,1,2,3,4,5。