别在不知道临时死区的情况下使用 JavaScript 变量

2019-10-16 admin

作者:Dmitri Pavlutin

翻译:疯狂的技术宅

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

未经允许严禁转载

我问一个简单的问题。以下哪个代码片段将会产生错误?

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

new Car('red'); // Does it work?

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

第二个先调用然后定义函数:

greet('World'); // Does it work?

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

正确答案:第一个代码段(带有类)将生成 ReferenceError。第二个工作正常。

如果你的答案与上述不同,或者在不知道底层发生了什么的情况下进行了猜测,那么你需要掌握临时死区(TDZ)。

TDZ 管理 letconstclass 语句的可用性。对于变量在 JavaScript 中的工作方式非常重要。

1.什么是_临时死区_(Temporal Dead Zone

让我们从一个简单的 const 变量声明开始。如果首先声明并初始化变量,然后访问它,那么一切都会按预期进行:

const white = '#FFFFFF';
white; // => '#FFFFFF'

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

white; // throws `ReferenceError`
const white = '#FFFFFF';
white;

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

在 TDZ 中访问了 white 之后,JavaScript 会抛出 ReferenceError: Cannot access 'white' before initialization

image.png

_TDZ(Temporal Dead Zone)_语义禁止在声明变量之前访问变量。它强制执行纪律:在声明之前不要使用任何东西

2. 受 TDZ 影响的语句

让我们看看受 TDZ 影响的语句。

2.1 const 变量

正如你已经看到的,const 变量在 TDZ 中声明和初始化行之前:

// Does not work!
pi; // throws `ReferenceError`

const pi = 3.14;

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

const pi = 3.14;

// Works!
pi; // => 3.14

2.2 let 变量

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

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

let count;

count = 10;

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

let count;

// Works!
count; // => undefined

count = 10;

// Works!
count; // => 10

2.3 class 语句

从简介中可以看出,在定义类之前不能使用它:

// Does not work!
const myNissan = new Car('red'); // throws `ReferenceError`

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

为了使它起作用,请在定义后保留类用法:

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

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

2.4 _constructor()_内部的 super()

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

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

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

constructor() 内部,this 在调用 super() 之前不能使用。

TDZ 建议调用父构造函数来初始化实例。完成之后,实例已准备就绪,你可以在子构造函数中进行调整。

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

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

2.5 默认函数参数

默认参数存在于 intermidiate 作用域内,与全局作用域和函数作用域分开。默认参数还遵循 TDZ 限制:

const a = 2;
function square(a = a) {
  return a * a;
}
// Does not work!
square(); // throws `ReferenceError`

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

要确保在声明和初始化之后使用默认参数。让我们使用特殊的变量 init ,该变量在使用前已初始化:

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

3. varfunctionimport 语句

与前面相反,varfunction 的定义不受 TDZ 的影响。它们在当前作用域内被提升。

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

// Works, but don't do this!
value; // => undefined

var value;

However, a function can be used regarding where it is defined: 但是,可以使用函数定义其位置:

// Works!
greet('World'); // => 'Hello, World!'

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

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

通常来说你对函数的实现不太感兴趣,而只是想调用它。所以有时在定义函数之前先调用该函数是有意义的。

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

// Works!
myFunction();

import { myFunction } from './myModule';

import 时,在 JavaScript 文件的开头加载模块的依赖项是一个好的做法。

4. TDZ 中的 typeof 行为

typeof 运算符可用于确定变量是否在当前作用域内定义。

例如,变量 notDefined 未定义,在这个变量上应用 typeof 运算符不会引发错误:

typeof notDefined; // => 'undefined'

由于未定义变量,因此 typeof notDefined 的值为 undefined

但是当与临时死区中的变量一起使用时,typeof 运算符有着不同的行为。在这种情况下,JavaScript 会报错:

typeof variable; // throws `ReferenceError`

let variable;

这个引用错误背后的原因是,你可以静态地(仅通过查看代码即可)确定已经定义了variable

5. TDZ 在当前作用域内运行

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

image.png

让我们来看一个例子:

function doSomething(someVal) {
  // Function scope
  typeof variable; // => undefined
  if (someVal) {
    // Inner block scope
    typeof variable; // throws `ReferenceError`
    let variable;
  }
}
doSomething(true);

There are 2 scopes: 有 2 个作用域:

  1. 函数作用域
  2. 定义 let 变量的内部块作用域

在函数作用域内,typeof variable 仅计算为 undefined。在这里, let variable 语句的 TDZ 无效。

在内部作用域中,在声明之前使用变量的 typeof variable 语句引发错误ReferenceError: Cannot access 'variable' before initialization。 TDZ 仅存在于此内部作用域内。

6. 结论

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

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

在我看来,当把良好的编码实践进入语言规范时,TDZ 就是其中的一个好东西。


本文首发微信公众号:前端先锋

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎继续阅读本专栏其它高赞文章:


[转载]原文链接:https://segmentfault.com/a/1190000020709071

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-77553.html

文章标题:别在不知道临时死区的情况下使用 JavaScript 变量

相关文章
vue 数组遍历方法forEach和map的原理解析和实际应用
一、前言 forEach和map是数组的两个方法,作用都是遍历数组。在vue项目的处理数据中经常会用到,这里介绍一下两者的区别和具体用法示例。 二、代码 1. 相同点 都是数组的方法 都用来遍历数组 两个函数都有4个参数:匿名函数中可传3...
2018-11-15
一些前端学习中好的书籍,整理
一、Javascript方面的书籍: 1 JavaScript权威指南(第6版):号称javascript圣经,前端必备;前端程序员学习核心JavaScript语言和由Web浏览器定义的JavaScript API的指南和综合参考手册; 2...
2015-11-12
html5+JavaScript教程-微信打飞机小游戏源码
js // JavaScript Document var c = document.getElementById("dotu"); var cxt = c.getContext("2d&q...
2015-11-12
JavaScript编辑器推荐
主流编辑器有SublimeText,Notepad++,webstorm等,是使用最广泛的编辑器,但也有一些JavaScript编辑器提供有着各自的特性和功能,适应不同人的需求,以下是几款优秀的编辑器,相信你一定能找到自己喜欢的。 1. W...
2015-11-12
js性能优化 如何更快速加载你的JavaScript页面
确保代码尽量简洁 不要什么都依赖JavaScript。不要编写重复性的脚本。要把JavaScript当作糖果工具,只是起到美化作用。别给你的网站添加大量的JavaScript代码。只有必要的时候用一下。只有确实能改善用户体验的时候用一下。 ...
2015-11-12
10个强大的纯CSS3动画案例分享
我们的网页外观主要由CSS控制,编写CSS代码可以任意改变我们的网页布局以及网页内容的样式。CSS3的出现,更是可以让网页增添了不少动画元素,让我们的网页变得更加生动有趣,并且更易于交互。本文分享了10个非常炫酷的CSS3动画案例,希望大家...
2015-11-16
2015年JavaScript或“亲库而远框架”
2014年过去了,作为一个JavaScript开发者很难满怀信心的去“挽回”一个特定的库或技术,即便是强大的Angular,似乎也因为最近的一些事情而动摇。 2014年10月的ng-europe会议上,Angular开发者团队透露了一个关于...
2015-11-12
v-charts | 饿了么团队开源的基于 Vue 和 ECharts 的图表工具
在使用echarts生成图表时,经常需要做繁琐的数据类型转化、修改复杂的配置项,v-charts的出现正是为了解决这个 痛点。基于Vue2.0和echarts封装的v-charts图表组件,只需要统一提供一种对前后端都友好的数据格式 设置简...
2018-05-24
JavaScript实现PC手机端和嵌入式滑动拼图验证码三种效果
PC和手机端网站滑动拼图验证码效果源码,同时包涵了弹出式Demo,使用ajax形式提交二次验证码所需的验证结果值,嵌入式Demo,使用表单形式提交二次验证所需的验证结果值,移动端手动实现弹出式Demo三种效果 首先要确认前端使用页面,比如...
2017-03-17
Angular2-primeNG文件上传模块FileUpload使用详解
近期在学习使用Angular2做小项目,期间用到很多primeNG的模块。 本系列将结合实战总结angular2-primeNG各个模块的使用经验。 文件上传模块FileUploadModule 首先要在使用该组件的模块内导入文件上传模块 ...
2017-03-09
回到顶部