Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
yaofly2012 opened this issue Feb 12, 2020 · 2 comments
Open

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

yaofly2012 opened this issue Feb 12, 2020 · 2 comments

Comments

@yaofly2012
Copy link
Owner

yaofly2012 commented Feb 12, 2020

变量生命周期

一、变量提升(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
@yaofly2012
Copy link
Owner Author

yaofly2012 commented Feb 12, 2020

let/const

一、语法规则

  1. 可以声明块级作用域变量;
    也可以声明函数作用域变量,但是不能声明全局作用域变量,如2。
  2. 全局环境下声明的let/const变量不会作为全局对象的属性;
<script>
                let aLet = 12;
                console.log(aLet ) // 12
                console.log(window.aLet ) // undefined
            </script>

哪定义的let变量在哪呢?
image

  • script标签隐含的构建了一个块作用域,全局生命的let/const变量都定义在这里。
  • 并且所有的script标签全局公用同一个块作用域:
    image
<script>
                let aLet = 12;
                console.log(aLet)
                console.log(window.aLet)
            </script>
            <script>
                let bLet = 22;
               // let aLet = 12; // 虽然不在同一个script标签里,但是也是重复声明变量
                console.log(aLet)
            </script>
  1. 同一个作用域下let/const变量不能重复声明,并且也不能跟var变量同名。
;(function(){
 let a = 12;
 {
    var a; // SyntaxError: Identifier 'a' has already been declared
 }
})()
  1. let/const变量需要先声明后使用。
a = 12;
let a; // ReferenceError: a is not defined
  1. constlet唯一的区别就是const声明时必须初始化,并且不能修改其值。

1.2 编码习惯

let/const不仅是可以声明块作用域变量,它们更是规范了编码习惯,即变量必须先声明后使用,所以不少人建议:

  1. 能用let的尽量不要用var;
  2. 能用const的尽量不要用let

二、暂时性死区-Temporal Dead Zone (TDZ)

处于未初始化状态的变量属于TDZ,只有let/const/class声明的变量才会出现TDZ。因为var和函数声明都因为变量提升规则不会出现未初始化的状态。一般有两种场景需要TDZ来解释:

  1. 使用未声明的let/const/class变量;
  2. let/const variable = 'value'语句中右值抛出异常,导致初始化失败(如下面的灵魂拷问)。

参考

  1. MDN let
  2. MDN const
  3. 我用了两个月的时间才理解 let
  4. Are variables declared with let or const not hoisted in ES6?

@yaofly2012
Copy link
Owner Author

yaofly2012 commented Feb 12, 2020

关于let/const的灵魂拷问

1. var、let、const的区别 ?

2. let变量有没有声明提升?

3. 如何理解 let x = x 报错之后,再次 let x 依然会报错?

4. 请写出如下代码的打印结果

var name = 'Tom';
(function() {
    if (typeof name == 'undefined') {
        var name = 'Jack';
        console.log('Goodbye ' + name);
    } else {
        console.log('Hello ' + name);
    }
})();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant