提升
JavaScript 的 **提升 (Hoisting)** 指的是解释器在代码执行之前,似乎将函数、变量、类或导入的 *声明* 移动到其 作用域 的顶部。
提升 (Hoisting) 不是 ECMAScript 规范中规范定义的术语。规范确实将一组声明定义为 HoistableDeclaration,但这仅包括 function
、function*
、async function
和 async function*
声明。提升通常也被认为是 var
声明的一个特性,尽管方式不同。通俗地说,以下任何行为都可能被视为提升
- 能够在其作用域内在声明该变量的行之前使用该变量的值。(“值提升”)
- 能够在其作用域内在声明该变量的行之前引用该变量,而不会引发
ReferenceError
,但该值始终为undefined
。(“声明提升”) - 变量的声明在其作用域内在声明它的行之前导致行为发生变化。
- 声明的副作用在评估包含它的其余代码之前产生。
上面四个函数声明以类型 1 行为提升;var
声明以类型 2 行为提升;let
、const
和 class
声明(也统称为 *词法声明*)以类型 3 行为提升;import
声明以类型 1 和类型 4 行为提升。
有些人更喜欢将 let
、const
和 class
视为非提升,因为 暂时性死区 (TDZ) 严格禁止在声明之前使用该变量。这种异议是可以理解的,因为提升不是一个普遍接受的术语。但是,暂时性死区会导致其作用域内其他可观察到的变化,这表明存在某种形式的提升
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 模块