JavaScript 中提升机制的秘密
提升(Hoisting)是 JavaScript 中最令人困惑的概念之一。它也是人们认为 JavaScript 是一种莫名其妙的语言的原因之一。但一旦你了解了它背后的原理,一切就都说得通了。
发动机内部
JavaScript 是一种解释型语言。这意味着 JavaScript 引擎会逐行执行代码,将其转换为机器代码(机器代码是计算机可以理解的二进制代码)。与 JavaScript 类似,Python 和 Perl 也是解释型语言。但 JavaScript 与这些编程语言的不同之处在于其代码提升机制。
试着猜测以下代码的输出结果:
console.log(a)
// Some other code
var a = 5;
如果你猜的是5,那就错了。
如果你猜会得到错误信息,那你也错了!
上述代码的实际输出是未定义的!是不是很奇怪?当提升操作成立时,一切就都说得通了。所以,让我们来弄明白其中的缘由。
JavaScript 的解释
我们已经知道 JavaScript 是逐行解释执行的。不过,这其中也存在一些复杂之处。你可以这样理解:JS 引擎会逐行遍历你的代码两次。第一次,引擎会遍历代码并进行提升和其他一些操作(例如添加缺失的分号)。第二次,它才会真正执行代码。
所以,提升(Hoisting)是指为变量和函数分配内存空间的过程。在代码开始执行之前,JavaScript 引擎会遍历代码,并为函数和变量分配内存块。变量的值不会被存储,但函数及其定义会被完整地存储起来。这就像引擎在实际运行代码之前,先在一张纸上写下需要跟踪的变量和函数一样。
让我们来检验一下我们的理解:
我们之前的例子:
console.log(a)
// Some other code
var a = 5;
所以,当我们的引擎第一次执行代码时,它会在一张纸上“写下”(这里用“纸”来比喻为变量预留一块内存)。引擎不会给变量赋值,所以默认情况下变量值为undefined。在完成这个“提升”(指在内存中)操作后,引擎会再次执行代码。在第一行,它遇到了变量a。然后,它查看 a 的内存引用。哦!a已经定义,所以它打印出当前undefined 的值。然后在下一行,a被重新赋值为 5。
我们再试一个:
b();
function b() {
console.log('b called!');
}
请注意,在进行函数提升时,变量仅存储其值为undefined,而函数则同时存储其定义。因此,引擎在遍历代码一次后,就知道有哪些变量,但不知道它们的值。它也知道有哪些函数以及每个函数的功能。因此,在上面的示例中,当我们调用函数b时,引擎已经知道存在这样一个函数以及它的功能。所以我们会得到输出“b 被调用了!”。
最后一个:
b();
console.log(a);
function b() {
console.log('b called!');
}
这个问题比较棘手,因为有一个小细节你可能容易忽略。这里,由于`a`没有定义,所以会报错。运行这段代码时,我们还会注意到另一个有趣的现象。让我们利用函数提升(hoisting)的知识一步步来分析。首先,函数 ` b`及其定义会被提升并存储到内存中。接下来是第二步。当解释器看到第一行代码时,它会调用函数`b`,屏幕上会显示“b called!” 。之后,在下一行代码中,由于`a`没有定义,会报错。需要注意的是,错误代码行上面的部分仍然会被执行,并且输出也会显示出来。这凸显了 JavaScript 作为解释型语言的一个重要特性。
综上所述,请注意以下几点:
- 与其他语言不同,JavaScript 不会因为在声明之前调用变量和函数而报错。
- 函数会完整执行,而变量会返回 undefined,直到它们被重新赋值。
ES6 特性
任何 JavaScript 博客都不能忽略 ES6 的特性。ES6 引入了两个新的关键字`let`和`const`来声明变量。使用 `let` 和 `const` 声明的变量也会被提升,但唯一的区别在于,对于 `let` 和 `const` 声明的变量,它们不会像 `var` 那样被初始化为`undefined`。下面的代码会抛出引用错误,因为变量`a`处于临时死区。如果它没有被提升,那么`a`的值将为 10。
a = 10;
console.log(a);
// Reference Error
let a = 5;
文章来源:https://dev.to/godcrampy/the-secret-of-hoisting-in-javascript-egi就这些啦!感谢阅读,祝您度过美好的一天😄