JS函数的执行时机

98 阅读2分钟

JS 函数执行的时机不同,得到的结果也会不同。

一般情况

let c = 1 
function fn(){console.log('c = ' + c)}
fn() 

执行步骤:

  • 声明变量 c 并为它赋值 1
  • 声明函数 fn
  • 执行 fn ,打印 c = 1
  • 结束
let c = 1
function fn(){console.log('c = ' + c)}
c = 2
fn()

执行步骤:

  • 声明变量 c 并为它赋值 1
  • 声明函数 fn
  • 2 赋值给 c
  • 执行 fn ,打印 c = 2
  • 结束
let c = 1
function fn(){console.log('c = ' + c)}
fn()
c = 2

执行步骤:

  • 声明变量 c 并为它赋值 1
  • 声明函数 fn
  • 执行 fn ,打印 c = 1
  • 2 赋值给 c
  • 结束

for 循环与 setTimeout

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

for 循环结束后,并没有得到预期的 1~6 ,而是打印出了六个 6 tu1

可以这样理解: 每一次 for 循环, setTimeout 都执行一次,但是里面的函数没有被执行,而是被放到了任务队列里。 for 循环执行了 6 次,就放了 6 次。因为 for 循环结束时 i=6 ,所以输出六个 6

  • 注: WindowOrWorkerGlobalScope 混合的 setTimeout() 方法设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。
  • 又问: setTimeout() 设置的定时器究竟什么时候会过期呢?
  • 答: 上文的代码中, setTimeout(()=>{console.log(c),0}) 可简化为 setTimeout(fn,0) 形式,在 for 循环执行完成后,它所设置的定时器就到期了, 0 代表零秒后执行(立即执行) setTimeout(fn,0)

一定要打印出 0、1、2、3、4、5

可以这样写:

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

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

tu2

还可以这样解决:

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

本文参考摘录了:

  1. 《JS 函数的执行时机》.黄思煜
  2. window.setTimeout