for循环里的定时器以及 var 和 let 的使用

1 篇文章 0 订阅
0 篇文章 0 订阅

一道for循环的经典题目

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

打印出:
在这里插入图片描述

看到这段代码的人可能首先会想到会在for里循环5次 i ,然后才会打印下面的字符串a,for循环里的打印结果会是 0 1 2 3 4 而不是出现5个 i = 5 这种意想不到的结果

但是这段代码实际跑起来的结果和我们预想的不一样。为什么会这样呢,其实这个问题并不难,遇到了,下次基本就不会错了,但是这个简单的代码内包含很多的js知识点。

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

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

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

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

也就是说,JS会任务setTimeout是一个异步操作,当console.log(‘a’)这个同步任务执行完毕之后,才会让他setTimeout执行

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

那么为什么不是 0 1 2 3 4 a呢 ,是因为同步代码执行完毕之后,i 在for循环里 i++ 加到5停止执行 ,所以 i 已经变成了5 ,这时候循环已经结束了。

于是就可以想象成这个样子,以便理解。
会先for循环,i = 5 然后打印 a 然后在打印 5 次 i

    for (var i = 0; i < 5; i++) {
    
    }
    
    console.log('a');
    
        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 呢 该怎么办?

可以使用ES6新增声明 let

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

打印结果如下
在这里插入图片描述

为什呢 let 可以呢?
先看以 let 和 var 两种声明方式的区别
1、没有变量提升,也就说必须提前声明才能使用,

   console.log(a);
    let a = 666
    //会报错Uncaught ReferenceError: Cannot access 'a' before initialization

2、不能重复声明

let a = 666
let a = 555555
//报错Uncaught SyntaxError: Identifier 'a' has already been declared

3、块级作用域

{ 
  var i = 5;
} 
console.log(i);  // 5

```javascript
{ 
  let i = 5;     // i变量只在 花括号内有效
} 
console.log(i);  // Uncaught ReferenceError: i is not defined

回到主题,因为let存在的块级作用域 ,for循环和定时器共享同一个作用域的同一个变量,但是let在循环变量中还有一个特殊功能:

每一次循环都会重新声明变量 i ,随后每一个循环都会使用上一个循环时结束的值来初始化这个变量 i 。let非常适合用于 for循环内部的块级作用域。JS中的for循环体比较特殊,每次执行都是一个全新的独立的块作用域,用let声明的变量传入到 for循环体的作用域后,不会发生改变,不受外界的影响。

i 虽然在全局作用域声明,但是在for循环体局部作用域中使用的时候,变量会被固定,不受外界干扰。
i 是循环体内局部作用域,不受外界影响。

也就是说,他也是同步任务 ,完成for的循环之后 ,定时器里的变量不会使用for循环之后的 i 的值,它会重新使用 i 变量 ,从0开始到最后依次执行。

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值