【重学ES6】为什么 let x = x 会报错?-- 暂时性死区

338 阅读3分钟

「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战

前言

最近在重新学习ES6,很多细节让我有种从来没学过js的错觉。

涉及知识点:变量提升、暂时性死区。 如果对这两个知识点已经非常熟悉的同学可直接跳过。

题目分析

其实就是把x赋值给x,我们知道赋值操作是从左到右的,所以显然执行赋值之前X自身也未必赋值过。

可能有点绕,话句话理解就是x未定义,x去给另一个变量赋值。(类似于 let y = x;)

image.png

执行报错了!

如果我们使用var声明并赋值呢?

image.png

用var声明就不会报错,是什么原因呢?

这里就是我们常常听说的所谓的变量提升,var属于es6之前的语法,存在变量提升的问题,es6推出let,const其中一个原因就是为了避免令人费解的变量提升问题。

当我们用var声明变量的时候,在该语句的 作用域 中会出现变量提升,默认声明会提升到执行上下文的最上面。

() => {
    console.log(a);
    var a = 123;
}
// 相当于
() => {
    var a;
    console.log(a);
    a = 123;
}

所以上面提到的问题,var a = a;由于变量提升的存在,a在赋值的时候已经存在了,只是为undefined

暂时性死区

下面来重点说道说道暂时性死区。

ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。 简单理解就是let不存在变量提升,声明之前的区间被锁定了,取不到在其后声明的变量。 这里还有一层含义,是被锁定。下面是一个典型的例子:

var temp = 123;
(function() {
    console.log(temp); //Uncaught ReferenceError
    let temp = 456;
})();

image.png 即使我们在上层作用域声明了temp变量,那么由于内部作用域中存在let声明,所以形成了暂时性死区,打印temp报错。

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

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

bar(); // 报错

先把y赋值给x,此时y还没声明。

“暂时性死区”也意味着typeof不再是一个百分之百安全的操作。

typeof x; // ReferenceError
let x;

上面代码中,变量x使用let命令声明,所以在声明之前,都属于x的“死区”,只要用到该变量就会报错。因此,typeof运行时就会抛出一个ReferenceError

作为比较,如果一个变量根本没有被声明,使用typeof反而不会报错。

总结

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

let 和 const 的出现在很大程度上就是为了弥补var的全局性、变量提升的问题。