JS 函数的执行时机

72 阅读1分钟

为什么如下代码会打印 6 个 6?

let i = 0
for(i = 0; i<6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}
/*
setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行
只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。
也就是当同步任务的函数和语句执行完后,0秒或者立刻执行setTimeout(fn,0)。
*/

在此段代码中,由于js是单线程语言,所以会首先执行完for循环,循环完毕,执行栈为空就马上执行setTimeout,执行六次

但是,若是把let i=0放到循环语句中

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

因为let变量的作用域只能在当前函数中,所以每次for循环生成的都是一个新的i, setTimeout里输出的i就是这个新的i,这个i是不会变化的,所以输出的就是正常的。

因为在for语句里用let声明变量是局部变量遵循块作用域,所以每次for循环执行时都会生成一个单独的作用域,i的值只会在当前循环内有效,每次会生成一个新的i,相当于有6个 i。 此时,每次执行setTimeout()时都会打印出对应的i,打印结果就是0、1、2、3、4、5了。

除了使用 for let 配合,还有

//闭包
let i 
for(i = 0; i<6; i++){
  !function(j){
      setTimeout(()=>{
        console.log(j)
      },0)
  }(i)
}
//利用 setTimeout 的第三个参数,将i传进去
let i 
for(i = 0; i<6; i++){
  !function(j){
      setTimeout(()=>{
        console.log(j)
      },0)
  }(i)
}
//利用 const 关键字
let i
for(i = 0; i<6; i++){
    const x = i
    setTimeout(()=>{
      console.log(x)
    })
}