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