TLDR
在 JavaScript 中,变量和函数声明都被提升。
初始化不是。
Hoisting 意味着 - 无论声明的词法位置如何 - 标识符从封闭代码区域(函数或块)的最开始就在语义上存在。
这意味着标识符(即变量名)在语义上出现在声明它们的代码行之前 - 如果只是为了保证同名标识符之间不会发生混淆。
请注意,在评估变量声明之前,开发人员可能无法访问提升的标识符(例如,如果使用let 声明)。 IE。函数或块开始执行后的一段时间。这一时期的正式名称是“时间死区”。
在 JavaScript 中,变量和函数声明都会被提升。
初始化不是。
对于使用var 声明的变量,其效果是可以将声明想象为位于封闭函数的最顶部,而不管词法(即在代码中)位置如何声明。
对于严格模式代码(function、function*、let、const、class)中的所有其他内容,可以想象声明位于封闭 block(可能是也可能不是函数),无论其声明的词法位置如何。
非严格代码有一个单独的、更复杂的set of rules 用于包含在块中的函数语句。见also。
对于使用var 声明的变量,变量本身(默认值为undefined)可用于从封闭执行上下文的顶部进行赋值和取消引用(读取其值)。
提升的var 声明会自动初始化为undefined。
对于其他所有内容,运行时都知道封闭块顶部的标识符,但在执行流程移过词法声明点之前,它不能用于赋值或取消引用。 IE。时间死区已经过去了。
提升的function/function* 声明立即使用提升的函数初始化。
请注意,在 JavaScript 中,初始化执行上下文(堆栈帧)的算法最后处理函数声明,这意味着 function 声明似乎比 var 声明“更高”。
这意味着如果一个函数声明和一个具有相同标识符的var在同一个函数中声明,则标识符将与function(而不是@987654342 @) 在封闭函数执行开始时。
对于let、const 和class,在控件移过变量的词法声明之前,不会初始化标识符。
这些let、const 和class 声明类型是在语言生命后期(ES 2015)中添加的。
语言设计者选择这种新行为是为了使 JavaScript 更易于理解,并避免在引入词法点声明之前允许赋值和取消引用的细微错误。
出于这个原因,在 JavaScript 中曾经有一个最佳实践,即应在其封闭函数的最顶部声明所述变量。
所以在你的示例代码中:
1 console.log(a);
2 var a = 4;
undefined
undefined
在执行之前,当代码的执行上下文(或堆栈帧)被实例化时,a 被提升到封闭范围的顶部。
a 是使用 var 声明的,因此在第 1 行,允许在 console.log(a) 内取消引用 a,并且将自动初始化的 undefined 值打印到控制台。
在第 2 行,代码会将4 分配给a(但不会打印任何内容)。
如果在浏览器控制台中运行,最终语句返回的值将由浏览器自动打印。
在这种情况下,var a = 4; 的结果是undefined,因此第二个undefined 会打印到控制台。