变量提升与暂时性死区

111 阅读2分钟

我们常说 “var声明会变量提升,let和const不会变量提升”。这种说法过于含糊,接下来详细解释一下。

提升

首先,比较官方一点的说法是“提升”而不是“变量提升”。

  • 提升:是指解释器在执行代码之前,似乎将函数变量导入的声明移动到其的顶部的过程。

所有提升是针对这四种目标:函数、变量、类和声明。

如果要对提升方式进行细分的话,提升可以分为引用提升(声明提升)值提升。下面针对四种提升类型,分别解释:

函数

函数声明是属于值提升:能够在声明函数之前在其作用域中使用该函数的值

say()
console.log(say)
function say(){
  console.log('hello')
}

比如上面的代码,就可以正常访问say的值。

但是下面的代码会报错:

/** ReferenceError */
say();

const say = ()=>{
  console.log('hello')
}

因为这个是函数表达式,不属于函数声明了,归于下述的变量声明这种情况。

变量

变量的提升方式是声明提升/引用提升。能够在声明变量之前在其作用域中引用该变量而不抛出ReferenceError,但是无法访问变量的值。var、let和const这三种声明方式有所区别:

var声明

可以在var声明前访问变量,但是在var变量初始化前,值始终是 undefined:

// undefined
console.log(a)

function say(){
  console.log(a)
}
// undefined
say()

var a = 1
// 1
say()

相当于直接把 var a; 这个声明提升到了作用域顶部,但是对值得初始化 a = 1 没有提升

const和let声明

const和let的提升行为是一致的。

先看代码

// ReferenceError
console.log(a)

const a = 1

访问a会报引用错误,有人就会以为 const和let不会进行变量提升,这个说法不严谨。

看下面的代码:

const say = ()=>{
  console.log(a)
}
const a = 1
// 1
say()

上面这段代码是正确的。如果真的不提升的话,在say函数体中是无法引用变量 a 的。

解释:const和let声明,会对变量的引用进行提升,在变量声明前就可以引用这个变量。但是! 在变量初始化前,对这个变量的访问会报错,这就是所谓的“暂时性死区”。在变量初始化后,是可以正常访问变量的。例如下面的代码:


const say = ()=>{
  console.log(a)
}
// ReferenceError,暂时性死区
console.log(a)
// ReferenceError,暂时性死区
say()

const a = 1
// 正常
say()

这里注意区分对变量引用和变量的访问,前者是词法概念/编译概念,后者是运行时概念。

class

class的提升行为和const/let一致