JS 函数的执行时机之setTimeout

setTimeout()的意思是设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。

例:

如下代码会打出6个6。

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

//打印结果6,6,6,6,6,6

其原因是因为:

setTimeout函数会优先执行之前的事件,最后再执行后续的事件。而之前的事件是i循环,显然得等i循环执行完毕再执行打印出i的这个函数,当i等于5时,因为小于6会再执行一次,直到i等于6,一共执行了6次,根据前面所诉会打出6个6,而非6个5


不过,为了让上面的结果变成 012345,可以使用如下的代码。

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

//打印结果0,1,2,3,4,5

在这个情况下,let关键字劫持了for循环的块作用域。通俗解释为JSforlet一起使用的时候会添加东西,每次进入循环的时候会多创建一个i,把i复制一份留在这个空间用于打印,这个所谓的i不是会变的i,是变化之前的拷贝,不跟随变化的i变化,所以一共变化了5次,不会到6,打印出012345

这使得setTimeout毫无意义。


我还提供了2种方法可以打印出012345的方法供大家参考。

1. 使用立即执行函数

for(var i = 0; i < 5; i++) {
    (function(i) {
        setTimeout(function () {
            console.log(i);
        });
    })(i)
}
console.log('a');

//打印结果0,1,2,3,4,5

和上面有点类似,这个是利用闭包的原理,闭包使一个函数可以继续访问它定义时的作用域。而这个新生成的作用域将每一次循环的当前i值单独保存了下来。

2. 使用try...catch语句

for(var i = 0; i < 5; i++) {
    try {
        throw(i)
    } catch(j) {
        setTimeout(function () {
            console.log(j);
        });
    }
}

//打印结果0,1,2,3,4,5

这是因为try...catch语句的catch后面的花括号是一个块作用域,和let的效果一样。所以在try语句块里抛出循环变量i,然后在catch的块作用域里接收到传过来的i,就可以将循环变量保存下来,实现类似闭包let的效果。

来源:https://juejin.cn/post/6844904036177707021
posted @ 2022-07-07 23:44  程序员小明1024  阅读(402)  评论(0编辑  收藏  举报