前言

let 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。

let是ES6新增的用于声明变量的关键字,用自己的话说,它的出现主要是用来替代var关键字的,那使用let声明的变量有什么特点呢?

特点

  • let声明的变量只在所处于的块级作用域有效

块级作用域

块级作用域,就是使用了大括号后产生的作用域,我们来看下面这个代码

1
2
3
4
if (true) {
let a = 10;
console.log(a); // 10
}

都在同一个大括号内,在同一块级作用域,可以正常打印;那如果打印和变量不在同一个大括号内,会正常打印吗?

1
2
3
4
if (true) {
let a = 10;
}
console.log(a);

这里将console.log(a)放在了if语句外,而a是在if语句中定义的,不在同一作用域内,打印的结果为 a is not defined

注意: 使用let关键字声明的变量才有块级作用域,使用var声明的变量不具备块级作用域特性

好处

防止循环变量变成全局变量

1
2
3
4
for (var i = 0; i < 3; i++) {

}
console.log(i);

当我们在用var做循环时,循环变量i在结束循环后依然能访问;但当我们改成let后,结束循环后就访问不到了

1
2
3
4
for (let i = 0; i < 3; i++) {

}
console.log(i); // i is not defined

不存在变量提升

在ES6之前,变量可以先使用再声明,存在变量提升。而使用let关键字就不会出现这种情况了,必须先是声明再使用,否则就会报错。

1
2
3
a = 10;
console.log(a); // 10
var a;
1
2
3
a = 10;
console.log(a); // 报错
let a;

暂时性死区

先看下面的这串代码

1
2
3
4
var num = 10;
if (true) {
console.log(num); // 10
}

正常打印,结果是10。

问题来了—— 那如果我在if语句中用let关键字再定义一个num后,还会正常打印吗?

1
2
3
4
5
6
var num = 10;
if (true) {
console.log(num);
let num = 20;
}
// Cannot access 'num' before initialization

这就是 let 的暂时性死区。当作用域内存在用 let 关键字定义的同名变量时,console.log(num) 不会再到此块级作用域外去寻找变量,而作用域内的num是在其后面定义的,就会出现这种情况,无法正常运行。

经典面试题

var 以下代码的输出结果

1
2
3
4
5
6
7
8
var arr = [];
for (var i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0]();
arr[1]();
  • 结果:2 2
  • 原因:for循环中循环两次分别给数组arr赋于一个函数,当数组 项arr[0]调用函数时,即arr0,由于函数体内(当前作用域)并没有声明i,因此通过作用域链向上查找,而这个循环采用var(var声明的变量不具备块级作用域特性),因此向上作用域即为全局作用域,全局作用域中的i的值为2,因此两次循环调用都为2。
  • 此题的关键点在于:每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的值。

let 下代码的输出结果

1
2
3
4
5
6
7
8
var arr = [];
for (let i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0]();
arr[1]();
  • 结果:0 1
  • 原因:跟上述的var声明的变量查找i的过程一样,由于当前作用域没有i,通过作用域链向上查找,由于采用let关键字声明的变量与块级作用域绑定,因此每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的值,也即0,1。
  • 此题的关键点在于变量i是全局的,函数执行时输出的时全局作用域下的i值。
更新于

请我喝[茶]~( ̄▽ ̄)~*

boW 微信支付

微信支付

boW 支付宝

支付宝

boW 贝宝

贝宝