提升
JavaScript 提升是指解释器在执行代码之前,似乎将函数、变量、类或导入的声明移动到其作用域的顶部的过程。
提升不是 ECMAScript 规范中规范定义的术语。规范确实将一组声明定义为可提升的声明,但这只包括 function
、function*
、async function
以及 async function*
声明。提升通常也被认为是 var
声明的一个特性,尽管方式不同。用通俗的话来说,以下任何行为都可以被视为提升:
- 能够在声明变量之前在其作用域中使用该变量的值。(“值提升”)
- 能够在声明变量之前在其作用域中引用该变量而不抛出
ReferenceError
,但值始终是undefined
。(“声明提升”) - 变量的声明导致在声明行之前的作用域中行为发生变化。
- 声明的副作用在评估包含该声明的其余代码之前产生。
前面说到的四种函数声明的提升表现为第 1 种行为;var
声明的提升表现为第 2 种行为;let
、const
和 class
声明(也称为词法声明)的提升表现为第 3 种行为;import
声明的提升表现为第 1 和第 4 种行为。
有些人更倾向于将 let
、const
和 class
视为不提升的,因为暂时性死区严格禁止在声明之前使用变量。这种看法是可以接受的,因为提升并不是一个普遍认同的术语。然而,暂时性死区可以导致其作用域内的其他可观察变化,这表明存在某种形式的提升:
const x = 1;
{
console.log(x); // ReferenceError
const x = 2;
}
如果 const x = 2
声明完全没有提升(即仅在执行时生效),那么 console.log(x)
语句应该能够读取上层作用域的 x
值。然而,由于 const
声明仍然“污染”了其定义的整个作用域,console.log(x)
语句读取的是 const x = 2
声明的 x
,但它尚未初始化,因此抛出 ReferenceError
。不过,从实用角度看,将词法声明视为不提升可能更有用,因为这些声明的提升并没有带来任何有意义的特性。
注意以下情况不属于提升:
{
var x = 1;
}
console.log(x); // 1
这里没有“在声明前访问”;只是因为 var
声明不是块级作用域的。
有关提升的更多信息,请参见:
var
、let
、const
提升——语法类型教程function
提升——函数教程class
提升——类教程import
提升——JavaScript 模块