面试题系列 —— 1、for 循环中的定时器问题

首先看一道例题:

 for (var i = 0; i < 5; i++) 
 	{ setTimeout(function () 
         { console.log(i); 
          }, 1000); 
    }
 //输出结果为:1秒后同时输出 5 5 5 5 5
  • 第一次看到这段代码的运行结果的时候,会给人一些疑问:

    1. for循环里面的打印结果为什么不是0,1,2,3,4,而是什么5个5这种奇怪的结果?

    2. 就算是5个5,但是为什么不是之间间隔1秒,而是同时输出结果?

      但是实际运行结果跟我们预期的不一样,原因就是因为这里涉及到了js的运行机制。

  • 这就总结一下其中的道理:

    JavaScript语言的一大特点就是单线程,也就是说同一时间只能做一件事情,所有的任务都需要等待上一个任务完成之后才会执行下一个任务。
    于是出现了同步和异步:

    a、同步就是相当于你放一首歌,他会一直跑下去,如果中间断了,他也不会去播放下一首歌,当通知你上一首播放完毕了,才会播放下一首个。播放歌就像等于进入了主线程,第二首歌就会在任务队列里等待主线程的执行。
    b、异步就相当于同时放两首歌,一首歌中断了,不会对另一首歌有影响。另一首歌还会一直播放下去。相当于两首歌都在任务队列里。当上面有同步任务在主线程内执行完毕,异步任务就可以进入主线程执行任务。

    只要主线程的任务空了,就会去找任务队列的任务去执行,JavaScript就是在不断重复这个过程的运行机制。

    任务队列中的事件,主要指定了回调函数,这些事件发生的时候就会进入任务队列等待主线程的执行完毕。

    • 总的来说:定时器不是同步的,他会自动的进入任务队列,等待同步任务的执行完毕才会执行,这也就是 为什么会打印出5次i = 5的原因。

    • 大白话解释:

      首先因为定时器的时间延迟执行特性,所以处在同步代码队列的for循环会在执行定时器之前将循环执行完。

      相应的图解:

      /*
      解析其详细过程
      1.当循环执行完毕时:i=4;i<5;i++ 注意最后一次后的i值是递增后的i++即是5;
      2.然后执行异步定时器时,他们会同时执行,他们并不是执行代码的时间延长了,
        而是执行时间被延迟了,5此循环会定义五个定时器,他们之间是一起执行的。
      */
      
      for (var i = 0; i < 5; i++) {
      
        }
        //这里拆解成这样,只是为了好理解
            setTimeout(function () {
            console.log('i = ' + i);
          });
              setTimeout(function () {
            console.log('i = ' + i);
          });
              setTimeout(function () {
            console.log('i = ' + i);
          });
              setTimeout(function () {
            console.log('i = ' + i);
          });
              setTimeout(function () {
            console.log('i = ' + i);
          });
      
      
  • 那么如何让其打印结果为 0 1 2 3 4 呢?

    for (let i = 0; i < 5; i++) {
          setTimeout(() => {
            console.log(i)
          }, i * 1000);			//为了间隔输出
        }
    
    

    为什么let可以呢?

    1. 没有变量提升
    2. 不能重复声明
    3. 块级作用域

    let每一次循环都会重新声明变量i,随后的每个循环都会使用上一个循环结束时的值来初始化这个变量 i 。它拥有着函数的作用域,在for循环的时候用let的时候这时因为有自己的作用域范围,作用域范围内,有着属于自己的i而不是共用的i那么它打印出来的时候就是属于自己的i,结果为0~4

深入解析

  • 当循环体内同步操作全部结束了那么开始唤醒队列中的异步操作,由于这时的i已经变成了5,那么这个时候就队列当中就类似这个场景

    var i=5
    () => {
            console.log(i)
          }
    () => {
            console.log(i)
          }
    () => {
            console.log(i)
          }
    () => {
            console.log(i)
          }
    () => {
            console.log(i)
          }
    

    此时他们全部共用一个i,那么当打印的时候执行起来就是5 5 5 5 5

    如果换成let的话

    由于let拥有作用域,那么他每一个i都有着自己对应的函数

    () => {i=0
            console.log(i)
          }
    () => {i=1
            console.log(i)
          }
    () => {i=2
            console.log(i)
          }
    () => {i=3
            console.log(i)
          }
    () => {i=4
            console.log(i)
          }
    

    他们操作自己的i那么打印出来的结果就是0 1 2 3 4

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值