0
点赞
收藏
分享

微信扫一扫

ES6(let 和 const 命令)知识点总结(一)


文章主要来源于阮一峰ES6第三版的内容,我只是把里面对我来说比较容易记错的点或者难理解的点,提取出来,归类整理一下,方便自己以后复习,如果想要了解更多的ES6这些知识的,建议去阮一峰的ES6的官方网站​​http://es6.ruanyifeng.com/​​

let命令:用来声明一个变量,和var非常类似

const命令:用来声明一个常量(是一个不可以变化的量)

用let声明变量的注意事项:

  1. 使用let声明的变量,所声明的变量只在命令所在的代码块内有效
  2. 使用let命令声明的变量在预解析的时候不会被提升
  3. let不允许在同一个作用域下声明已经存在的变量

用const声明常量的注意事项

  1. 所声明的变量只在其所在的代码块内有效
  2. 声明的变量不会在预解析的时候提升
  3. 不能声明已经被声明的常量或者变量
  4. 声明的时候必须赋值
  5. 声明的常量如果储存简单的数据类型(string、number、Boolean)时候不可以改变其值,如果储存是对象,那么引用不可以被改变,至于对象里面的数据如何变化是没有关系的。


暂时性死区:在代码块内,使用let和const命令声明变量之前,该变量或常量都是不可用的,这在语法上称为”暂时性死区”(temporal dead zone,简称 TDZ)。

ES6 明确规定,如果区块中存在​​let​​和​​const​​命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。ES6 规定暂时性死区和​​let​​、​​const​​语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。

总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

注意:

在没有let之前,typeof运算符是百分百安全的,永远不会报错。现在这一点不成立了,因为,在代码块内,使用let声明变量之前,该变量都是不可用的

typeof x; // ReferenceError
let x;

有些“死区”比较隐蔽,不太容易发现。

function bar(x = y, y = 2) {
return [x, y];
}

bar(); // 报错


上面代码中,调用​​bar​​函数之所以报错(某些实现可能不报错),是因为参数​​x​​默认值等于另一个参数​​y​​,而此时​​y​​还没有声明,属于”死区“。如果​​y​​的默认值是​​x​​,就不会报错,因为此时​​x​​已经声明了。

function bar(x = 2, y = x) {
return [x, y];
}
bar(); // [2, 2]

另外,下面的代码也会报错,与​​var​​的行为不同。

// 不报错
var x = x;

// 报错
let x = x;
// ReferenceError: x is not defined

上面代码报错,也是因为暂时性死区。使用​​let​​声明变量时,只要变量在还没有声明完成前使用,就会报错。上面这行就属于这个情况,在变量​​x​​的声明语句还没有执行完成前,就去取​​x​​的值,导致报错”x 未定义“。

为什么需要块级作用域?

ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。

第一种场景,内层变量可能会覆盖外层变量。

var tmp = new Date();

function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}

f(); // undefined

上面代码的原意是,​​if​​代码块的外部使用外层的​​tmp​​变量,内部使用内层的​​tmp​​变量。但是,函数​​f​​执行后,输出结果为​​undefined​​,原因在于变量提升,导致内层的​​tmp​​变量覆盖了外层的​​tmp​​变量。

第二种场景,用来计数的循环变量泄露为全局变量。

var s = 'hello';

for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}

console.log(i); // 5

上面代码中,变量​​i​​只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。

难以理解的知识点:

下面的代码如果使用​​var​​,最后输出的是​​10​​。

var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10

上面代码中,变量​​i​​是​​var​​命令声明的,在全局范围内都有效,所以全局只有一个变量​​i​​。每一次循环,变量​​i​​的值都会发生改变,而循环内被赋给数组​​a​​的函数内部的​​console.log(i)​​,里面的​​i​​指向的就是全局的​​i​​。也就是说,所有数组​​a​​的成员里面的​​i​​,指向的都是同一个​​i​​,导致运行时输出的是最后一轮的​​i​​的值,也就是 10。如果使用​​let​​,声明的变量仅在块级作用域内有效,最后输出的是 6。

var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6


上面代码中,变量​​i​​是​​let​​声明的,当前的​​i​​只在本轮循环有效,所以每一次循环的​​i​​其实都是一个新的变量,所以最后输出的是​​6​​。你可能会问,如果每一轮循环的变量​​i​​都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量​​i​​时,就在上一轮循环的基础上进行计算。另外,​​for​​循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc


上面代码正确运行,输出了 3 次​​abc​​。这表明函数内部的变量​​i​​与循环变量​​i​​不在同一个作用域,有各自单独的作用域。

易出错的例子:


不能在函数内部重新声明参数。

function func(arg) {
let arg; // 报错
}

function func(arg) {
{
let arg; // 不报错
}
}

块级作用域

function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}

上面的函数有两个代码块,都声明了变量​​n​​,运行后输出 5。这表示外层代码块不受内层代码块的影响。如果两次都使用​​var​​定义变量​​n​​,最后输出的值才是 10。

举报

相关推荐

0 条评论