JS函数的执行时机

180 阅读2分钟

JavasScript引擎是基于事件驱动和单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行程序,即主线程。

JS函数的调用的时机不同,所得的结果也是不尽相同的

1.解释为何如下代码会打印出6个6?

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

setTimeout方法用途是在指定毫秒数后调用执行函数,其执行的时机并不是立即执行,而是尽快,意指,在当前操作执行完毕后立马执行。

上述的代码是指在for循环结束后立马执行setTimeout里面的函数,打印出i的值。

解释

setTimeout是一个异步的任务,它会过一会在执行,在执行for循环的时候,setTimeout会被放置到任务队列等待,一旦当前操作for循环执行完毕,然后就能继续执行setTimeout。然而,在for循环的过程中变量i的值一直在不断的变化,最后一次for循环执行后,变量i的值赋值为6,此时setTimeout开始执行,而此时所有任务队列里的setTimeout得到的变量i的值俱为6,所以会打印出6个6。

如何理解异步操作: 由于js引擎是单线程,那么js只会同一时间做一件事情,那么会带来一个问题就是如果一个操作的时间会特别长,那么接下去的操作就会一直等待着,就会阻塞事件的进程。为了解决js引擎将这些需要耗时阻塞事件执行的事件暂时存放到一个任务队列中。等到当前环境代码执行完毕后,再执行任务队列的代码。总的来说,异步操作是不会等待结果的代码。

2.写出让上述代码能打印出0,1,2,3,4,5的方法

1.利用for和let的配合

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

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

2.利用闭包

闭包可以让函数访问外部变量,保存外部变量的值

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

3.利用 setTimeout 的第三个参数,将i传进去

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

4.利用const关键字

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