理解es6中的暂时性死区

在ES6的新特性中,最容易看到TDZ作用就是在let/const的使用上,let/const与var的主要不同有两个地方:

  • let/const是使用区块作用域;var是使用函数作用域

  • 在let/const声明之前就访问对应的变量与常量,会抛出ReferenceError错误;但在var声明之前就访问对应的变量,则会得到undefined

console.log(aVar) // undefined
console.log(aLet) // causes ReferenceError: aLet is not defined
var aVar = 1
let aLet = 2

根据ES6标准中对于let/const声明的章节13.3.1,有以下的文字说明:

The variables are created when their containing Lexical Environment is instantiated but may not be accessed inany way until the variable’s LexicalBinding is evaluated.

意思是说由let/const声明的变量,当它们包含的词法环境(Lexical Environment)被实例化时会被创建,但只有在变量的词法绑定(LexicalBinding)已经被求值运算后,才能够被访问。

注: 这里指的"变量"是let/const两者,const在ES6定义中是constant variable(固定的变量)的意思。

说得更明白些,当程序的控制流程在新的作用域(module, function或block作用域)进行实例化时,在此作用域中的用let/const声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,也就是对声明语句进行求值运算,所以是不能被访问的,访问就会抛出错误。所以在这运行流程一进入作用域创建变量,到变量开始可被访问之间的一段时间,就称之为TDZ(暂时死区)。

以上面解说来看,以let/const声明的变量,的确也是有提升(hoist)的作用。这个是很容易被误解的地方,实际上以let/const声明的变量也是会有提升(hoist)的作用。提升是JS语言中对于变量声明的基本特性,只是因为TDZ的作用,并不会像使用var来声明变量,只是会得到undefined而已,现在则是会直接抛出ReferenceError错误,而且很明显的这是一个在运行期间才会出现的错误。

用一个简单的例子来说明let声明的变量会在作用域中被提升,就像下面这样:

let x = 'outer value'

(function() {
  // 这里会产生 TDZ for x
  console.log(x) // TDZ期间访问,产生ReferenceError错误
  let x = 'inner value' // 对x的声明语句,这里结束 TDZ for x
}())

在例子中的IIFE里的函数作用域,变量x在作用域中会先被提升到函数区域中的最上面,但这时会产生TDZ,如果在程序流程还未运行到x的声明语句时,算是在TDZ作用的期间,这时候访问x的值,就会抛出ReferenceError错误。

在let与const声明的章节13.3.1接着的几句,说明有关变量是如何进行初始化的:

variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If aLexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.

这几句比较重点的部份是关于初始化的过程。以let/const声明的变量或常量,必需是经过对声明的赋值语句的求值后,才算初始化完成,创建时并不算初始化。如果以let声明的变量没有赋给初始值,那么就赋值给它undefined值。也就是经过初始化的完成,才代表着TDZ期间的真正结束,这些在作用域中的被声明的变量才能够正常地被访问。

下面这个例子是一个未初始化完成的结果,它一样是在TDZ中,也是会抛出ReferenceError错误:

let x = x

因为右值(要被赋的值),它在此时是一个还未被初始化完成的变量,实际上我们就在这一个同一表达式中要初始化它。

注: TDZ最一开始是为了const所设计的,但后来的对let的设计也是一致的,例子中都用let来说明会比较容易。

注: 在ES6标准中,对于const所声明的识别子仍然也经常为variable(变量),称为constant variable(固定的变量)。以const声明所创建出来的常量,在JS中只是不能再被赋(can't re-assignment),并不是不可被改变(immutable)的,这两种概念仍然有很大的差异。

  • 15
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值