JS函数的执行时机

190 阅读2分钟

?

直接上代码!

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

这个代码按照我们的逻辑来讲,先让i=0,让后for循环依次打印出i的值来,直到不满足i<6这个条件,并且打印的时间间隔为0秒。

咳咳,按照逻辑来讲,系统应该会依次打印出0、1、2、3、4、5这六个值,可是实际结果却并不是这样的

可以看出,这段代码打印出来的居然是6个6!这究竟是为什么呢?

逻辑错了吗?这个其实逻辑没有错,但是底层代码的实现过程相对于我们人而言其实是透明的,机器会严格按照代码的顺序来实现,我们逻辑上的0秒与系统底层的0秒其实是不同的。逻辑上的0秒是完全理想的0,而实际代码执行中只能无限接近0秒这个时间间隔,然后很不幸,系统处理代码是很快的(对于我们而言是透明的),在这个无限接近0秒的时间间隔中,代码已经执行完6次了,i停在了i=6这儿,然后执行延迟0秒的代码console.log(i)

JS函数的执行时机其实还是代码的实现顺序,机器是不会有错的!(个人理解,有错望各位指正!)

!

那么其实问题又双叒来了,我要怎么才能让i顺序输出0、1、2、3、4、5来呢?试试下面这段代码吧!

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

哇!居然又能打印顺序打印出了呢!

这个其实是和for+let声明变量有一个特殊的行为,这个行为指出变量在循环过程中不止被声明一次,每次迭代都会声明。随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。

但是不止有上面这一种方法实现打印出0、1、2、3、4、5的方法,下面三种也可以

  • 引入IIFE(立即执行函数)
let i = 0
for(i = 0; i < 6; i++){
 (function(i){
   setTimeout(()=>{
     console.log(i)
   },0)
 })(i)
}
  • 利用setTimeout的第三个参数
let i = 0
for(i = 0; i < 6; i++){
  setTimeout((j)=>{
    console.log(j)
  },0,i)
}

setTimeout()如果想给回调函数传递参数,直接在第二个参数delay后面加上附加的参数。

参考语法var timeoutID = scope.setTimeout(function[, delay, param1, param2, ...]);

  • 利用闭包
let i = 0
for(i = 0; i < 6; i++){
  setTimeout(((j) => {
    return ()=>{
        console.log(j)
  }
    })(i),0)
}

如果大家还有其他的方法,欢迎留下,一起交流。