《JS函数的执行时机》

159 阅读2分钟

JS函数的执行时机

先来看看下面的代码,看看会打印出什么?0,1,2,3,4,5还是6,6,6,6,6,6?

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

答案是:6个6。

下面我们下来解释一下setTimeout函数的作用:假设,小强正在家打游戏,他妈妈叫小强去吃饭,小强说马上就就去。那么小强是立刻停止打游戏,去吃饭还是先打完这局,再去吃饭呢?这个“马上就是”就相当于setTimeout,吃饭就是console.log,打游戏就是for循环。

为啥会出现这种情况你=呢?这就要跟JS的执行机制说起了,setTimeout函数是一个异步函数,那么这段代码的执行过程就变成了,先走完整个循环,因为每次循环的时候就会把定时器(setTimeout)里的console.log丢去待命状态,等待循环结束,就有个6个console.log的待命了,这是i也变成了6,才开始执行6个console.log(i),所以最终会打印出6个6。换句话说就是函数什么时候开始调用,什么时候才开始查询参数的值

如何解决这个问题呢?依然要打印出0,1,2,3,4,5呢?。在ES6新语法中,出现的let声明就能解决这个问题。我们将上面的代码稍微修改一下就可以:

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

仅仅是改变了let的位置,将for循环个let声明结合使用,就起到了神奇的作用,这个是JS对于小白的幻想的实现,是对于小白的大爱。

那除了以上这个中方法之外还有没有其他的不用let的方法呢?还有两种方法可以实现

  1. 一种是使用setTimeout的另一个参数
    for(var i= 0; i<6; i++){
        setTimeout(function(i) {
            conosole.log(i)
        },0,i)
    }
  1. 利用闭包的原理,使用立即执行函数,将定时器包起来,第一个for循环结束,就把当前的i当作参数传递给立即执行函数并将结果保存起来;第二次for循环再通过定时器打印出来。
for(var i=0; i<6; ++i) { 
    !function(j) { 
        setTimeout(function(){
            console.log(j) 
            }, 0) 
      }(i) 
  }