提升

JavaScript 的 **提升 (Hoisting)** 指的是解释器在代码执行之前,似乎将函数、变量、类或导入的 *声明* 移动到其 作用域 的顶部。

提升 (Hoisting) 不是 ECMAScript 规范中规范定义的术语。规范确实将一组声明定义为 HoistableDeclaration,但这仅包括 functionfunction*async functionasync function* 声明。提升通常也被认为是 var 声明的一个特性,尽管方式不同。通俗地说,以下任何行为都可能被视为提升

  1. 能够在其作用域内在声明该变量的行之前使用该变量的值。(“值提升”)
  2. 能够在其作用域内在声明该变量的行之前引用该变量,而不会引发 ReferenceError,但该值始终为 undefined。(“声明提升”)
  3. 变量的声明在其作用域内在声明它的行之前导致行为发生变化。
  4. 声明的副作用在评估包含它的其余代码之前产生。

上面四个函数声明以类型 1 行为提升;var 声明以类型 2 行为提升;letconstclass 声明(也统称为 *词法声明*)以类型 3 行为提升;import 声明以类型 1 和类型 4 行为提升。

有些人更喜欢将 letconstclass 视为非提升,因为 暂时性死区 (TDZ) 严格禁止在声明之前使用该变量。这种异议是可以理解的,因为提升不是一个普遍接受的术语。但是,暂时性死区会导致其作用域内其他可观察到的变化,这表明存在某种形式的提升

js
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。尽管如此,将词法声明描述为非提升可能更有用,因为从功利主义的角度来看,这些声明的提升不会带来任何有意义的功能。

请注意,以下不是提升的形式

js
{
  var x = 1;
}
console.log(x); // 1

这里没有“在声明之前访问”;这仅仅是因为 var 声明的作用域不限于块。

有关提升的更多信息,请参阅

另请参阅