变量提升与暂时性死区(进阶必备知识)

1,168 阅读3分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

前言

let  和 const  相关的考题,若要就  let  和 const   关键字特性本身提问,确实很难考察出候选人的水平。但是,它们背后所牵扯出的变量提升、暂时性死区等知识点,对一些同学来说却具有相当的挑战性——这块知识属于大家多少都听说过、能说上那么两句,但基本没几个人能说清楚的类型。

变量提升

TIP 👉 在 ES2015 之前,JS 引擎用 “var” 这个关键字声明所有的变量。

在 “var” 时代,有一个特别的现象:不管我们的变量声明是写在程序的哪个角落,最后都会被提到作用域的顶端

console.log(num)
var num =  1

这段代码不会报错,反而会输出一个 undefined。这就是因为变量的声明被“提升”了,它等价于这样:
var num;
console.log(num)
num =  1

在函数作用域里,也会有类似的现象

function getNum()  {
    console.log(num)
    var num =  1
}

这里同样输出undefined,这是因为函数内部的变量声明会被提升至函数作用域的顶端。

变量提升的原理

TIP 👉 这和js 编译过程有关。JS也是有编译阶段的,它和传统语言的区别在于,JS不会早早地把编译工作做完,而是一边编译一边执行。简单来说,所有的JS代码片段在执行之前都会被编译,只是这个编译的过程非常短暂(可能就只有几微妙、或者更短的时间),紧接着这段代码就会被执行。

JS 和其他语言一样,都要经历编译和执行阶段。正是在这个短暂的编译阶段里,JS 引擎会搜集所有的变量 声明,并且提前让声明生效。至于剩下的语句,则需要等到执行阶段、等到执行到具体的某一句的时候才会生效。这就是变量提升背后的机制。

被禁用的变量提升

let 和 const 区别于 var 的一个重要特性——它们不存在变量提升。

现在我们把上面例子里的 num 用 let 来声明:

console.log(num)
let num =  1

image.png

这样做是因为,早期的声明提升机制,其实容纳了很多程序员的误操作——那些忘记被声明的变量无法在开发阶段被明显地察觉出来,而是以  undefined   这样危险的形式藏匿在你的代码里。为了减少运行时错误,防止暗中使坏的undefined 带来不可预知的问题,ES6 特意将“声明前不可用”这一点做了强约束。

用const声明也一样。

暂时性死区

来看一段代码

var name = '青莲使者'
{
    me = 'li'
    let me
}

运行结果报错

这是因为 ES6 中有明确的规定:如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域,如果我们在声明前尝试调用它,就会报错

这一段会报错的危险区域,有一个专属的名字,叫”暂时性死区“。其实暂时性死区的本质:当我们进入当前作用域时,let 或者 const 声明的变量已经存在了——它们只是不允许被获取而已。要想获取他们,必须得等到代码执行到声明的位置。