Skip to content

JS-ES6-let/const & 变量生命周期 #103

Open
@yaofly2012

Description

@yaofly2012

变量生命周期

一、变量提升(Hoisting)

变量声明或者函数声明移动到当前作用域的最顶部的过程。一般是指var声明变量或者函数声明。

二、变量生命周期

var,函数声明,let, const, class都可以声明变量,JS引擎在处理变量时主要分为3个阶段。
image

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
  1. 声明阶段都会被提升,但此时变量还不能引用;
  • 声明中同名冲突?
  1. var变量可以先使用(值为undefined)后声明;
  2. 函数可以先调用后声明;
  3. let/const/class则必须先声明(代码里的声明)后使用。

不过“变量提示”是从ES3就有的叫法,我们也一般是指var和函数声明变量的变量提升行为。基于这点有时我们也称let/const/class变量不存在变量提升。
为了避免疑惑,以后如果再提到“变量提升”时,则需要先明确是哪种声明变量方式。

四、Function declarations should not be placed in blocks

Stack overflow: Function declarations should not be placed in blocks. Use a function expression or move the statement to the top of the outer function

参考:

  1. JavaScript Variables Lifecycle: Why let Is Not Hoisted
  2. 我用了两个月的时间才理解 let

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions