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的方法呢?还有两种方法可以实现
- 一种是使用
setTimeout
的另一个参数
for(var i= 0; i<6; i++){
setTimeout(function(i) {
conosole.log(i)
},0,i)
}
- 利用闭包的原理,使用立即执行函数,将定时器包起来,第一个for循环结束,就把当前的i当作参数传递给立即执行函数并将结果保存起来;第二次for循环再通过定时器打印出来。
for(var i=0; i<6; ++i) {
!function(j) {
setTimeout(function(){
console.log(j)
}, 0)
}(i)
}