blog icon indicating copy to clipboard operation
blog copied to clipboard

let 声明提升了吗?

Open nanyang24 opened this issue 6 years ago • 1 comments

不同的经历阅历会对同一件事物产生不同的看法

一切都是一个 Circle,最后都会归于原点。

let 声明提升了吗

前言

言归正传,有学过 JS 的都知道 var 可以声明变量,常见的还有ES6的 let/const,以及声明函数的 function 关键字。那么肯定也都了解过 var 的声明提升,那么 let/const是否有声明提升呢??

很多人,至少是我,在之前是粗浅地认为 let/const 是没有声明提升的,这样认为的原因是在这两个关键字声明变量之前,是不可以访问这些变量的。

如果之前的理解和我一样,那么你要记住了:JS中所有变量的声明(包括函数),都有声明提升的规则

何为 声明提升

在真正理解刚才那句话之前,我们来规范一下之前认为 var 的声明提升。

在一个作用域内,解释器会找到此作用域内所有 var 声明的变量,并且在作用域顶部将它们创建,并初始化赋值为 undefined,在最终声明变量的位置进行赋值操作。

// dosomthing

var a = 1

实际上解释器是这样执行的:

var a = undefined

// dosomthing

a = 1

注意了,我们之前所谓的 var 声明提升,是包含 创建和初始化的

不过这说明不了 let 有声明提升呀

所以再看以下代码

x = y = "global";
(function() {
    x; // undefined
    y; // Reference error: y is not defined

    var x = "local";
    let y = "local";
}());

解释一下,在全局环境中声明了x,y,并赋值为 'global'。然后在函数作用域内,在var x = "local";语句之前访问 x 会得到 undefined;然而在 let y = "local";语句之前访问 y 会报错,不过报错也证明了let是存在提升的,不然访问 y 就是访问函数外部作用域的 y 了。但怎么解释这种奇怪的行为呢?

  • 这是因为传统的声明变量,例如 var/function 都是会进行 创建初始化的提升(function特殊一点,赋值也会一并提升)
  • 而ES6:let/const/class 都只是会在 创建 阶段进行提升,而在真正初始化之前,是不能访问变量的,这一区间也就是 Temporal Dead Zone/暂时性死区。

代码解释一下

{
// dosomething
  let x = 1
  x = 2
}

在这个块作用域内,解释器的过程就是:

  1. 找到所有用 let 声明的变量,并在当前 hoisting 中 创建 这些变量
  2. 开始执行代码 dosomething(注意现在还没有初始化)
  3. 执行 x = 1,将 x 初始化 为 1(这并不是一次赋值,如果代码是 let x,就将 x 初始化为 undefined)
  4. 执行 x = 2,对 x 进行「赋值」

而 TDZ 就是

{
let x 
  'start a TDZ'
// dosomething
  'end a TDZ'
  let x = 1
  x = 2
}

在真正let声明变量初始化之前是不可以访问的。

const

与 let 的行为大致相同,只是没有赋值操作,即初始化值之后不能进行赋值操作。

总结

总结一下:

  1. let 声明会提升到块顶部
  2. 从作用域顶部到该变量的初始化语句的区域,叫做 TDZ(暂时性死区),在 TDZ 内使用该变量,JS 就会报错

上述的「创建、初始化和赋值」英文即「declare、initialised、assign」,为了与 变量提升 作区分,把declare比作创建。

参考

这一句tc39委员会成员的解释我觉得很精妙

In JavaScript, all binding declarations are instantiated when control flow enters the scope in which they appear. Legacy var and function declarations allow access to those bindings before the actual declaration, with a "value" of undefined. That legacy behavior is known as "hoisting". let and const binding declarations are also instantiated when control flow enters the scope in which they appear, with access prevented until the actual declaration is reached; this is called the Temporal Dead Zone. The TDZ exists to prevent the sort of bugs that legacy hoisting can create.

  1. hoisting-vs-tdz
  2. Are variables declared with let or const not hoisted in ES6?
  3. let hoisting? #767
  4. MDN关于let说明的修改
  5. 我用了两个月的时间才理解 let

最后

我觉得有很多困惑的地方在于没有具体确定的词语去描述它,多看不同版本的资料会让自己有更深刻和全面的认识,最终转化为自己的理解。

nanyang24 avatar Jun 20 '18 16:06 nanyang24

写的好

alberthuang24 avatar Jul 18 '18 17:07 alberthuang24