美文网首页Web前端之路程序员
不可不知的JavaScript 变量 Temporal Dead

不可不知的JavaScript 变量 Temporal Dead

作者: 1024译站 | 来源:发表于2019-10-09 16:36 被阅读0次

先问个简单的问题,下面哪段代码会报错?

  1. 先创建类实例,然后定义这个类:
new Car('red'); // Does it work?

class Car {
  constructor(color) {
    this.color = color;
  }
}
  1. 先调用函数,再定义这个函数:
greet('World'); // Does it work?

function greet(who) {
  return `Hello, ${who}!`;
}

正确答案是:第一段代码会抛出ReferenceError异常,第二段代码可以正常运行。

如果你答错了,或者只是猜对了但不知道背后的原理,那么你应该了解下什么是Temporal Dead Zone (TDZ)

TDZ 负责管理let, const, 和class 语句的可用性。这就需要知道JavaScript变量是如何工作的。

1. 什么是 Temporal Dead Zone

先看一个简单的 const变量声明。如果先声明变量,然后访问它,那么一切正常:

const white = '#FFFFFF';

white; // => '#FFFFFF'

现在试着在声明之前访问white 变量:

white; // 抛出`ReferenceError`异常
const white = '#FFFFFF';

white;

这段代码里,在const white = '#FFFFFF'语句出现之前,变量 white 处于Temporal Dead Zone。

在TDZ内访问white,JavaScript会抛出异常ReferenceError: Cannot access 'white' before initialization.

image.png

Temporal Dead Zone 语义禁止在未声明之前访问变量。它强调了这样的规则:不要在未声明前使用任何东西

2. TDZ影响到的语句

我们来看看哪些语句会受 TDZ 的影响。

2.1 const 变量

前面已经看到了,const 变量在声明和初始化前位于 TDZ:

// 跪了!
pi; // throws `ReferenceError`
const pi = 3.14;

必须要在声明后使用const 变量:

const pi = 3.14;

// 稳了!
pi; // => 3.14

2.2 let 变量

let 变量声明同样受 TDZ 影响:

// Does not work!
count; // throws `ReferenceError`
let count;

count = 10;

同样,要在声明之后才能使用:

let count;

// 稳了!
count; // => undefined
count = 10;

// Works!
count; // => 10

2.3 class 语句

前面说了,不能在定义class之前使用它:

// 跪了!
const myNissan = new Car('red'); // throws `ReferenceError`
class Car {
  constructor(color) {
    this.color = color;
  }
}

需要先定义再使用:

class Car {
  constructor(color) {
    this.color = color;
  }
}

// 稳了!
const myNissan = new Car('red');myNissan.color; // => 'red'

2.4 constructor() 里的super()

如果一个类继承自另一个类,构造函数里的 this 在调用super() 之前位于TDZ:

class MuscleCar extends Car {
  constructor(color, power) {
    this.power = power;   
    super(color); 
 }
}

// 跪了!
const myCar = new MuscleCar('blue', '300HP'); // `ReferenceError`

constructor()内部, thissuper() 调用之前不可用。要改成这样:

class MuscleCar extends Car {
  constructor(color, power) {
    super(color);    
    this.power = power;  
  }
}

// 稳了!
const myCar = new MuscleCar('blue', '300HP');
myCar.power; // => '300HP'

2.5 函数默认参数

默认参数处于一个中间作用域,不同于全局作用域和函数作用域。默认参数同样会受到TDZ的限制:

const a = 2;
function square(a = a) {
  return a * a;
}
// 跪了!
square(); // throws `ReferenceError`

参数a在表达式a = a的右边被使用,同时未声明。这会产生引用错误。
要确保在声明和初始化后再使用默认参数,比如这样:

const init = 2;
function square(a = init) {
  return a * a;
}
// 稳了!
square(); // => 4

3. var, function, import 语句

跟前面提到的语句相反,varfunction 并不受 TDZ 影响,因为会被作用域提升。

如果你在声明之前访问变量var ,只是得到 undefined值:

// 能运行,但不建议这么做
value; // => undefined
var value;

而函数不管在哪定义,都可以直接使用:

// 稳了!
greet('World'); // => 'Hello, World!'
function greet(who) {
  return `Hello, ${who}!`;
}

// 稳了!
greet('Earth'); // => 'Hello, Earth!'

有意思的是, import 的模块也能被提升:

// 也很稳!
myFunction();
import { myFunction } from './myModule';

尽管如此,还是建议在 JS 文件最顶部加载模块依赖。

4. TDZ 的typeof 行为表现

typeof 操作符可以用来判断当前作用域的某个变量是否已定义。
例如,变量notDefined没有定义。对它使用typeof 操作符并不会抛错:

typeof notDefined; // => 'undefined'

但是对Temporal Dead Zone里的变量使用typeof 操作符,会有不同的表现:

typeof variable; // throws `ReferenceError`
let variable;

这是因为,通过静态分析(用眼睛看)可以看出 variable 并不是未定义。

5. 当前作用域的TDZ 行为

Temporal Dead Zone 只会影响到声明语句所在的作用域内的变量。

image.png

我们来看个例子:

function doSomething(someVal) {
  // 函数作用域
  typeof variable; // => undefined 
  if (someVal) {
    // 内部块级作用域
    typeof variable; // 抛出`ReferenceError`   
    let variable;
  }
}
doSomething(true);

这里有两个作用域:

  1. 函数作用域
  2. let 变量所在的块级作用域
    函数作用域内,typeof variable 直接返回undefined。这里的let 变量语句不归TDZ管。
    在块级作用域内, typeof variable 使用了未声明的变量,于是抛出异常 ReferenceError: Cannot access 'variable' before initialization。TDZ只在该作用域内起作用。.

6. 结论

TDZ是一个重要的概念,它影响着constletclass 语句的可用性。它不允许在声明之前使用变量。
相反, var变量沿用了旧有行为,可以在声明之前使用变量。但是应该避免这么做。

我觉得TDZ 是个好东西,它从语言规范层面指导了最佳编码实践。

相关文章

  • 不可不知的JavaScript 变量 Temporal Dead

    先问个简单的问题,下面哪段代码会报错? 先创建类实例,然后定义这个类: 先调用函数,再定义这个函数: 正确答案是:...

  • let和const命令

    let和var的一些区别 var存在变量提升,let不存在变量提升 暂时性死区(temporal dead zon...

  • javaScript中的暂性死区

    Don't Use JavaScript Variables Without Knowing Temporal D...

  • let

    使用let的注意事项 1.块级作用域2.同一作用域中不允许重复声明3.暂存死区(Temporal dead zon...

  • 关于TDZ暂存死区

    前段时间在阮一峰微博上偶然学习到关于TDZ的东东,于是辗转于个各个学习网站并记录学习。 Temporal Dead...

  • 变量提升和函数提升

    摘自《你不知道的JavaScript上卷》KYLE SIMPSON著 变量提升 引擎会在解释 JavaScript...

  • 7 js 变量

    13 局部 JavaScript 变量在 JavaScript 函数内部声明的变量(使用 var)是局部变量,所以...

  • JavaScript 变量作用域和内存问题

    按照ECMAScript的定义,JavaScript的变量与其他语言的变量有很大区别。JavaScript变量松散...

  • Javascript 变量、函数的声明

    javascript变量 全局变量和局部变量按照变量的作用域来区分,和大多数编程语言类似,javascript变量...

  • JS的基本语法--变量及类型

    1. 基本语法 声明(创建) JavaScript 变量在 JavaScript 中创建变量通常称为“声明”变量。...

网友评论

    本文标题:不可不知的JavaScript 变量 Temporal Dead

    本文链接:https://www.haomeiwen.com/subject/mskgpctx.html