JS 函数的执行时机

298 阅读1分钟

JS 函数的执行时机

为什么是6个6?

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

上面的代码,console.log(i)打印出的结果是6、6、6、6、6、6,并不是我们惯性理解的0、1、2、3、4、5。原因就出现在setTimeout()这个定时器上,该定时器在一段时间后,执行一个函数或者指定的一段代码。

  1. 先声明变量i,赋值为0。
  2. for循环:变量i初始值为0,满足条件i < 6,执行循环语句setTimeout(()=>{console.log(i)}, 0),意思是,过一会儿输出i的值。然后执行i++i的值变为1。
  3. i = 1,满足条件i < 6,执行循环语句,过一会儿输出i的值。
  4. i = 2,满足条件i < 6,执行循环语句,过一会儿输出i的值。
  5. i = 3,满足条件i < 6,执行循环语句,过一会儿输出i的值。
  6. i = 4,满足条件i < 6,执行循环语句,过一会儿输出i的值。
  7. i = 5,满足条件i < 6,执行循环语句,过一会儿输出i的值。
  8. i = 6,不满足条件i < 6跳出循环
  9. 此时一共累积了6条 “过一会儿输出i的值”。
  10. 现在i的值已经为6,最终输出6个6。

JavaScript 传统上是单线程的,此线程称为主线程(main thread)。一个线程是一个基本的处理过程,每个线程一次只能执行一个任务。每个任务顺序执行,只有前面的结束了,后面的才能开始。

所以单线程意味着所有任务需要排队,上例中,setTimeout()里面的console.log(i)便是在for循环整个完成后,才能开始执行。

但只用做如下的一点改动,便可以最终输出0、1、2、3、4、5。

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

这两段代码的区别仅仅只有,第二段代码把let写在了for循环里,for(let i = 0; i < 6; i++){循环体}这句话的圆括号之间,有一个隐藏的作用域,在每次执行循环体之前,JS 引擎会自动把i在循环中重新声明及初始化一次

这样通过6次循环便得到了我们理想中的0、1、2、3、4、5,这6个不同的i的值,等待for循环结束后,开始执行setTimeout(),输出0、1、2、3、4、5。

还有什么方法可以打印出0、1、2、3、4、5 ?

使用立即执行函数

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

使用setTimeout()的第三个参数

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

使用const关键字

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

学习参考

我用了两个月的时间才理解 let