前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >知道临时死区你才能更好的使用 JS 变量

知道临时死区你才能更好的使用 JS 变量

作者头像
前端小智@大迁世界
发布2020-05-11 17:53:13
1.3K0
发布2020-05-11 17:53:13
举报
文章被收录于专栏:终身学习者终身学习者

作者:Dmitri Pavlutin 译者:前端小智 来源:dmitripavlutin

首先,来个一个简单的问题。下列哪段代码会产生错误:

第一个创建实例,然后定义使用的类:

代码语言:javascript
复制
new Car('red'); // 是否会报错?

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

或者先于函数定义之前调用函数:

代码语言:javascript
复制
greet('World'); // 是否会报错?

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

正确的答案是:第一个代码片段会报 ReferenceError: Cannot access 'Car' before initialization 错误。第二个代码正常运行。

如果你的答案与上述不同,或者你在不知道这背后的原理是什么而进行了猜测,那么你需要掌握临时死区(TDZ)的知识。

TDZ 管理 letconstclass 语法的可用性。变量在 JS 中的工作方式非常重要。

1.什么是临时死区

咱们先从一个简单的 const 变量声明开始。 首先声明并初始化变量,然后访问它,一切正常运行:

代码语言:javascript
复制
const white = '#FFFFFF';

white; // => '#FFFFFF'

那如果在 声明之前访问 white 变量,会怎么样》

代码语言:javascript
复制
white; // throws `ReferenceError`

const white = '#FFFFFF';

white;

const white = '#FFFFFF' 语句之前的代码行中,变量 white 位于临时死区

TDZ 中访问 white 后,JS抛出ReferenceError: Cannot access 'white' before initialization

临时死区语义禁止在变量声明之前访问它。它加强了顺序:在声明之前不要使用任何东西。

2.受 TDZ 影响的声明

来看看受 TDZ 影响的声明。

2.1 const变量

如前所述,const 变量位于声明和初始化行之前的 TDZ 中:

代码语言:javascript
复制
// 无法工作
pi; // throws `ReferenceError`

const pi = 3.14;

咱们必须在声明之后使用 const 变量:

代码语言:javascript
复制
const pi = 3.14;

// Works!
pi; // => 3.14
2.2 let 变量

在声明行之前,let 声明语句也会受到 TDZ 的影响:

代码语言:javascript
复制
// 无法工作
count; // throws `ReferenceError`

let count;

count = 10;

同样,仅在声明之后使用 let 变量:

代码语言:javascript
复制
let count;

// Works!
count; // => undefined

count = 10;

// Works!
count; // => 10
2.3 class 的声明

正如在介绍中看到的,在定义 class 之前不能使用它:

代码语言:javascript
复制
// 无法工作
const myNissan = new Car('red'); // throws `ReferenceError`

class Car {
  constructor(color) {
    this.color = color;
  }
}
2.4 构造函数内部的 super()

如果在构造函数中调用 super()之前扩展父类,则此绑定位于 TDZ 中。

代码语言:javascript
复制
class MuscleCar extends Car {
  constructor(color, power) {
    this.power = power;
    super(color);
  }
}

// Does not work!
const myCar = new MuscleCar('blue', '300HP'); // `ReferenceError`

在构造 constructor() 中,在调用super()之前不能使用 this

TDZ 建议调用父构造函数来初始化实例。这样做之后,实例就准备好了,就可以在子构造函数中进行调整。

代码语言:javascript
复制
class MuscleCar extends Car {
  constructor(color, power) {
    super(color);
    this.power = power;
  }
}

// Works!
const myCar = new MuscleCar('blue', '300HP');
myCar.power; // => '300HP'
2.5 默认函数参数

默认参数存在于一个中间作用域中,与全局作用域和函数作用域分离。默认参数也遵循 TDZ 限制。

代码语言:javascript
复制
const a = 2;
function square(a = a) {
  return a * a;
}
// Does not work!
square(); // throws `ReferenceError`

在声明表达式 a = a之前,在表达式的右侧使用参数 a,这将生成关于 a 的引用错误。

确保在声明和初始化之后使用默认参数。 咱们可以使用一个特殊的变量 init,该变量在使用前已初始化:

代码语言:javascript
复制
const init = 2;
function square(a = init) {
  return a * a;
}
// Works!
square(); // => 4

3.var, function, import 语句

与上述陈述相反,varfunction 定义不受 TDZ 的影响。它们被提升到当前的作用域顶部。

如果在声明之前访问 var 变量,则只会得到一个 undefined 的变量

代码语言:javascript
复制
// 正常运行, 但不要这样做!
value; // => undefined

var value;

但是,可以根据函数的定义位置来使用它:

代码语言:javascript
复制
// 正常工作
greet('World'); // => 'Hello, World!'

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

// 正常工作
greet('Earth'); // => 'Hello, Earth!'

通常,咱们一般对函数的实现不太感兴趣,而只是想调用它。 因此,有时在定义函数之前先调用该函数是有意义的。

有趣的是,import 模块也被提升了。

代码语言:javascript
复制
// 正常工作
myFunction();

import { myFunction } from './myModule';

当然,建议将 import 写在文件开头,以便读写方法。

4. TDZ 中的 typeof 行为

typeof 操作符用于确定是否在当前作用域内定义了变量。

例如,未定义变量 notDefined。对该变量应用 typeof 操作符不会引发错误:

代码语言:javascript
复制
typeof notDefined; // => 'undefined'

因为变量没有定义,所以 typeof notDefined 的值为 undefined

但是 typeof 操作符在与临时死区中的变量一起使用时具有不同的行为。在本例中,JS 抛出一个错误:

代码语言:javascript
复制
typeof variable; // throws `ReferenceError`

let variable;

此引用错误背后的原因是您可以静态地(仅通过查看代码)确定已经定义了该变量。

5. TDZ 在当前作用域内采取行动

临时死区在声明语句所在的作用域内影响变量。

来看看例子:

代码语言:javascript
复制
function doSomething(someVal) {
  // 函数作用域
  typeof variable; // => undefined
  if (someVal) {
    // 内部块使用域
    typeof variable; // throws `ReferenceError`
    let variable;
  }
}
doSomething(true);

有 2 个作用域:

  • 函数作用域
  • 定义 let 变量的内部块作用域

在函数作用域中,typeof variable 的计算结果为 undefined。在这里,let 变量语句的 TDZ 没有作用。

在内部作用域中,typeof variable 语句在声明之前使用一个变量,抛出一个错误。ReferenceError:在初始化之前不能访问‘variable’,TDZ 只存在于这个内部作用域内。

6.总结

TDZ 是影响 constletclass 语句可用性的重要概念。它不允许在声明之前使用变量。

相反,可以在声明之前使用 var 变量时,var 变量会继承较旧的行为,应该避免这样做。

在我看来,TDZ是语言规范中良好的编码实践之一。

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://dmitripavlutin.com/ja...

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.什么是临时死区
  • 2.受 TDZ 影响的声明
    • 2.1 const变量
      • 2.2 let 变量
        • 2.3 class 的声明
          • 2.4 构造函数内部的 super()
            • 2.5 默认函数参数
            • 3.var, function, import 语句
            • 5. TDZ 在当前作用域内采取行动
            • 6.总结
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档