Open
Description
变量生命周期
一、变量提升(Hoisting)
变量声明或者函数声明移动到当前作用域的最顶部的过程。一般是指var
声明变量或者函数声明。
二、变量生命周期
var
,函数声明,let
, const
, class
都可以声明变量,JS引擎在处理变量时主要分为3个阶段。
1. 声明阶段(Declaration)
- 这里的“声明”跟我们平时说的“声明”不一样,这里是JS引擎向作用域声明变量。
- 此时的变量处于暂时性死区(TDZ)。
即只声明还未初始化(unitialized )的变量 - 声明阶段还会检查变量是否已经被声明过了
如果发生重复声明,对于let/const/class
无法进行重复声明的变量则会抛异常。
SyntaxError: Identifier 'XXX' has already been declared
2. 初始化阶段
- 分配内存空间并跟作用域里的变量做个绑定,变量被初始化为
undefined
。
但是对于let/const variable = 'value'
语句是以value
作为初始值的:
// Reference 的 value
ExpressionT value = EmptyExpression();
if (Check(Token::ASSIGN)) {
// 用 assignment expression 的结果 initialize
value = ParseAssignmentExpression();
} else {
// 没有 Initializer
// 对于 const 会抛出 Syntax Error;对于 let 会设为 undefined
value = GetLiteralUndefined();
}
- 只有初始化后的变量才可以被引用,否则报引用异常。
- 初始化失败Case
对于let/const variable = 'value'
定义的变量初始化阶段和赋值是一条语句完成的,如果此时取右值发生异常,会导致变量初始化失败,此时变量依旧处于未初始化状态(暂时性死区)。
3. 赋值阶段
把右值赋值给变量
4. var
和函数声明
的不同
三、“变量提升”的本质
技术上变量提升是指变量的生命周期的某些阶段被提升了。var
,函数声明和let/const/class
声明的变量“变量提升”的行为不一致:
声明变量方式 | 声明阶段 | 初始化阶段 | 赋值阶段 |
---|---|---|---|
函数声明 | 提升 | 提升 | 提升 |
var | 提升 | 提升 | X |
let/const/class | 提升 | X | X |
声明阶段
都会被提升,但此时变量还不能引用;
- 声明中同名冲突?
var
变量可以先使用(值为undefined)后声明;- 函数可以先调用后声明;
let/const/class
则必须先声明(代码里的声明)后使用。
不过“变量提示”是从ES3就有的叫法,我们也一般是指var
和函数声明变量的变量提升行为。基于这点有时我们也称let/const/class
变量不存在变量提升。
为了避免疑惑,以后如果再提到“变量提升”时,则需要先明确是哪种声明变量方式。