Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

理解ES6中的TDZ(暂时性死区) #30

Open
songning0605 opened this issue Aug 16, 2020 · 0 comments
Open

理解ES6中的TDZ(暂时性死区) #30

songning0605 opened this issue Aug 16, 2020 · 0 comments
Labels

Comments

@songning0605
Copy link
Owner

参考:理解ES6中的TDZ(暂时性死区)

什么是TDZ

Temporal Dead Zone(TDZ)是ES6(ES2015)中对作用域新的专用语义。TDZ名词并没有明确地写在ES6的标准文件中,一开始是出现在ES Discussion讨论区中,是对于某些遇到在区块作用域绑定早于声明语句时的状况时,所使用的专用术语。

let/const和var的区别

在ES6之前,JS的scope只有两种,全局作用域和函数作用域,但是在ES6种出现了块级作用域,即使用let/const可以定义块级作用域。 那么在ES6的新特性中,最容易看到TDZ作用的就是使用let/const的使用上面。 let/const与var的主要不同有两个地方:

  • let/const是使用区块作用域;var是使用函数作用域
  • 在let/const声明之前就访问对应的变量与常量,会抛出ReferenceError错误;但在var声明之前就访问对应的变量,则会得到undefined
console.log(Vname); // undefined;
console.log(Lname); // ReferenceError
var Vname = 'xiaoxiao';
let Lname = 'xiaoxiao';

实践证明当我们在未声明之前使用var定义的变量时会得到undefined,但是在使用let未定义的变量时会抛出错误。因为ES6中的let声明的变量是不存在变量提升的作用。

var x = 10;
if (true) {
    x = 20; // ReferenceError
    let x;
}

ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。 总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ结束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}

上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。

typeof的“死区”陷阱

我们都知道使用typeof 可以用来检测给定变量的数据类型,也可以使用它来判断值是否被定义。当返回undefined时表示值未定义; 但是在const/let定义的变量在变量声明之前如果使用了typeof就会报错

typeof x; // ReferenceError
let x;

因为x是使用let声明的,那么在x未声明之前都属于暂时性死区,在使用typeof时就会报错。所以在使用let/const进行声明的变量在使用typeof时不一定安全。

typeof y; // 'undefined'

但是可以看出,如果我们只是用了typeof而没有去定义,也是不会报错的,从这里可以看出只要没有明确规定使用const/let定义之前就是不会出错。

传参的“死区”陷阱

例如下面一段代码,我们在使用

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和let声明的另外一种区别。

// 不报错
var x = x;

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

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

总结

ES6 规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant