JS 函数的执行时机

·  阅读 118

函数执行的时机不同,运行结果也不同。

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

复制代码

上面的代码中, console.log(i)打印的结果是6个6,而不是0,1,2,3,4,5。 这里便用到了调用时机,下面有图有真相。


1621171900(1).png

以上代码用到了一个 setTimeoutsetTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行。所以就有了上面代码会打印出6个6的情况。


可以这样理解
  • 因为JS是单线程的,单线程就意味着所有任务需要排队,前一个任务结束后,才会执行后一个任务。
  • 同步任务:上一件事情没有完成,继续处理上一件事情,只有上一件事情完成了,才会做下一件事情 –> JS中大部分都是同步编程。
  • 异步任务:规划要做一件事情,但是不是当前立马去执行这件事情,需要等一定的时间,这样的话,我们不会等着他执行,而是继续执行下面的操作。

所以在for循环时

  1. 先赋值i的值为0
  2. 然后判断是否 i<6 ? 如果满足条件会继续进入下一个循环。
  3. setTimeout()代码到了这里会等一会执行,这里便会跳过继续执行i++
  4. 执行i++,此时i赋值为1.
  5. 继续判断i<6,满足进入第二个循环.
  6. .
  7. .
  8. .
  9. 直到执行i++,i的值是6时,不满足i<6.
  10. 跳出循环
  11. 执行第一次循环的setTimeout() //打印出i
  12. 执行第二次循环的setTimeout() //打印出i
  13. 执行第三次循环的setTimeout() //打印出i
  14. 执行第四次循环的setTimeout() //打印出i
  15. 执行第五次循环的setTimeout() //打印出i
  16. 执行第六次循环的setTimeout() //打印出i
  17. 结束,打完收工现在可以看出,由于setTimeout()的执行时间为for语句执行后,所以每次打印出的结果都为6

到了这里便需要介绍以下setTimeout()

setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行

代码到了这里时会把函数放在任务队列中,当JS引擎线程空闲时并达到指定时间时,才会把函数放到JS引擎线程中执行。

就算setTimeout()的时间参数为0时,也必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。


但是往往就有很多愣头青,来骗,来胡搅蛮缠必须让它打印出来0,1,2,3,4,5,没错就是在座的各位,😑😑, 没问题安排

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

复制代码

上面代码,便会打印出0,1,2,3,4,5.

:---------:1621175231(1).png

理解

简单来讲就是把let写在for循环里面,这样可以单纯为每个i创造一个块级作用域,也就是复制6个i。

这里的变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新变量。

虽然每一轮循环时i都会重新声明,但是JS内部引擎会记住上一轮循环的值,然后复制了一个i的值,在初始化上一轮变量i的同时,保证了下一轮循环可以在上一轮循环的基础上进行计算,每次循环的i其实都是一个新的变量。

这样,在6次循环依次得出0/1/2/3/4/5,等待for循环结束后,setTimeout()开始执行,打印出0 1 2 3 4 5。


除了letfor配合之外还有以下别的办法打印出0,1,2,3,4,5

使用立即执行函数

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((i)=>{
  console.log(i)
},0,i)
}

复制代码

使用const关键字

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

复制代码
以上三种方法都可以打印出0,1,2,3,4,5,网络搜寻
分类:
前端
分类:
前端