JS 函数的执行时机

152 阅读1分钟
let i = 0
for(i = 0; i<6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

上面这段代码会打印 6 个 6。

这是因为每次 for 循环的时候,setTimeout 都执行一次,但是里面的函数没有被执行,而是被放到了任务队列里,等待执行。只有主线上的任务执行完,才会执行任务队列里的任务。当 for 循环结束后,此时 i 的值已经变成了6,这时回调函数按时间顺序先进先出执行。

要想实现打印结果为每次循环时的 i 值,即 0 1 2 3 4 5,可采用以下方案。

let 方案

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

JS 在 for 和 let 一起使用的时候,每次循环的时候多创建一个 i(复制当前循环中的 i),并把这个 i 给到定时器。所以主线程 for 循环执行完了,回调函数中的 i 是每次 for 循环时的 i。

闭包方案

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

通过立即执行函数和闭包,将 i 的变量驻留在内存中,当输出 j 时,引用的是外部函数的变量值i,i的值是根据循环来的,执行 setTimeout 时已经确定了里面的输出。

setTimeout 附加参数方案

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

附加参数,一旦定时器到期,它们会作为参数传递给回调函数。