ES6-理解let与块级作用域

ES6-理解 let 与块级作用域

举个栗子

1
2
3
4
5
6
7
8
9
10
11
for (var i = 0; i < 3; i++) {
//此处i默认为var类型,不写亦可
setTimeout(function () {
console.log(i); //依次输出3,3,3
}, 1000);
}
for (let j = 0; j < 3; j++) {
setTimeout(function () {
console.log(j); //依次输出0,1,2
}, 1000);
}

输出:
图片

这是为什么呢?这就涉及到块级作用域作用域链

首先,我们需要了解–

  • 函数声明时会确定且保存其相关作用域链,无关该函数于何处被调用或如何被调用,也就是说,当定义一个函数时,实际上是保存了一个作用域链。
  • setTimeout方法不会阻止循环,而是会将延时调用的函数放到最后执行,等待循环执行完毕后,根据作为参数传入的时延开始倒计时,倒计时结束后将函数放入队列中执行

要证明这一点,我再举个栗子–

1
2
3
4
5
6
for (let j = 0; j < 3; j++) {
console.log("Crushdada");
setTimeout(function () {
console.log(j); //依次输出0,1,2
}, 1000);
}

结果:
图片

* 理解`setTimeout`异步,参考:
* [JS异步执行之setTimeout 0的妙用](https://www.jianshu.com/p/91121cf5924f?fileGuid=QwWdqvVp86RWTcVv)
* 深入理解JS单线程&异步任务:[setTimeout运行机制](https://blog.csdn.net/qingwenxiutong/article/details/52397676?fileGuid=QwWdqvVp86RWTcVv)

下面让我们来分别解析一下

两个循环的执行过程–

var 声明的 for 循环:

  • i 是一个全局变量,在全局作用域内
  • 上面提到setTimeout方法会在循环结束后执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var i = 0;
var i = 1;
var i = 2;
var i = 3; //退出循环后还会执行一次自加操作
{ //第一次循环
function () {
console.log(i);
}  
}
{ //第二次循环
function () {
console.log(i);
}
}
{ //第三次循环
function () {
console.log(i);
}
}
  • 每次调用函数时,JS 会沿着作用域链逐层地向外寻找”i“这个变量
  • 上面说到作用域链的头部是当前函数(的活动对象),因此 JS 会先看看函数作用域内有没有
  • 没有,就会向外一层,发现全局作用域下有变量 i
  • 但是由于 3 次循环结束后,全局变量 i 的值已经被覆盖了
  • 因此,只会输出 i 最后的值 3

let 声明的 for 循环:

  • i 不是一个全局变量,具有块级作用域(花括弧内),只在块级作用域内存在
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   //第一次循环
{
let j = 0;
function () {
console.log(j);
}
}
//第二次循环
{
let j = 1;
function () {
console.log(j);
}
}
//第三次循环
{
let j = 2;
function () {
console.log(j);
}
}
  • 每次执行到循环体中的setTimeout方法,该方法都会将调用的回调函数放入“任务队列中”,等待主线程(执行栈中)的事件全部执行完毕后,执行队列头的事件。
  • 第一次循环后,任务队列中只有一个被setTimeout方法放入的回调函数,其作用域链中记录的是 i 的初始值0
  • 由于 let 声明的变量只存在于块级作用域内,因此每一次循环体执行完毕会**销毁**该变量,然后在 for 循环出的新块let 声明一个新的变量 j,按 for 循环原本既定的顺序为其赋值,然后执行循环体
  • 因此第二次循环时,任务队列中的回调函数的作用域链中,记录的是新创建的,重新被赋值为1的变量 i
  • 正是由于块级作用域相互独立互不影响,才不会覆盖**j 的值,就此,我有点理解为什么let 能防止数据污染了**(还有 es6 规定 let 不能重复声明这一点)
  • 综上,输出结果为 0、1、2

参考文章:

ES6-let变量--特性 ElementUI & MintUI

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×