JavaScript - 函数的执行时机

76 阅读4分钟

时机不同,结果不同。

举例说明:

例一:

let a = 1
function fn(){
  console.log(a)
}

这里不会打印出任何东西,因为函数只是进行了声明,但是没有被调用。

例二:

let a = 1
function fn(){
  console.log(a)
}
fn()  // 1

会打印出 1,这里是对例一进行完善

上述代码表示:

  • 声明变量 a 并赋值为 1
  • 声明一个函数 fn ,内容是打印出 a
  • 调用函数 fn
  • a 是 1 ,所以打印出 1

例三:

let a = 1
function fn(){
  console.log(a)
}
a = 2
fn() // 2

结果打印出 2

上述代码表示:

  • 声明变量 a 并赋值为 1
  • 声明一个函数 fn ,内容是打印出 a
  • a 赋值为 2
  • 调用函数 fn
  • 在调用函数 fn 时,a 已经被赋值为 2 ,所以在调用函数 fn 时, a 是 2

例四:

let a = 1
function fn(){
  console.log(a)
}
fn()
a = 2

结果打印出 1

上述代码表示:

  • 声明变量 a 并赋值为 1
  • 声明一个函数 fn ,内容是打印出 a
  • 调用函数 fn
  • a 赋值为 2
  • 这里在调用函数 fn 时,a 的值还是 1 ,被调用后再赋值的2 。所以在调用函数 fn 时, a 是 1

例五:

let a = 1
function fn(){
  setTimeout(()=>{
    console.log(a)
  },0)
}

fn()
a = 2

结果打印出 2

上述代码表示:

  • 声明变量 a 并赋值为 1
  • 声明一个函数 fn ,内容是当前代码执行完毕后,再打印出 a
  • 调用函数 fn
  • a 赋值为 2
  • 因为setTimeout表示等到当前代码执行完毕后,再执行。所以调用函数fn时并没有立即执行console.log(a) (如果没有setTimeout而立即执行的话,此时 a 是 1 ,fn()调用打印出也是 1 ;同例四)。而等到执行的时候 a 已经变成 2 了,此时打印会打印出 2

关于setTimeout

setTimeout表示尽快执行,不是立即执行。可以理解为等到当前代码执行完毕后,再执行。

通俗的举例来说:比如我正在打游戏,这时候妈妈让我去吃饭,我嘴上说马上,但其实会把游戏打完之后再立马跑去吃饭。

特殊情况

例一:

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

结果会打印出 6 个 6

理由:setTimeout表示尽快执行,即会等到当前代码的for循环执行完了,再去执行console.log(i)。而for循环执行完之后,i已经是6了,所以会打印出6个6 。

例二:

如果将let i = 0放到for循环里面,打印结果会有不同

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

结果会打印出 0、1、2、3、4、5

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

除了使用 for let 配合,还有什么其他方法可以打印出 0、1、2、3、4、5?

方法一:

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

方法二:(闭包)

for(let i=0;i<6;i++){
  !function(i){
     setTimeout(()=>{console.log(i)},0)  
  }(i)
}
  • 声明匿名函数 function(value){} 包裹 setTimeout()
  • 然后再匿名函数前加上运算符 ,防止生成新的全局变量(避免污染全局)
  • 在匿名函数后加个 () 立即调用,并把 i 当作参数 value 传入匿名函数进行调用

方法三:

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

使用setTimeout 的第三个参数(setTimeout的第三个参数作用,它就是当作setTimeout第一个函数的参数)

  • 这个参数可以将自身传给第一个参数,也就是匿名函数function(value)中,作为所需要的参数value,value可默认不写。
  • 而 i 共传入6次(0,1,2,3,4,5),通过匿名函数即可打印出。
  • 通常不写第三个参数,如果默认不写第三个参数,则不会传入函数
  • 由于每次传入的参数是从for循环里面取到的值,所以会依次输出0~5

方法四:(const关键字)

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

资料来源:

饥人谷

JS 函数的执行时机(深入理解6个6)