JavaScript:提升内部

2021-04-04

image-20210404173016225

JavaScript提升内部

引言

在这篇文章中,我们将学习另一个称为Hoisting的基本JavaScript概念。

由于本系列的重点是了解每个概念的基础知识和内在原理,因此我们将在这里尝试探索起重的内在原理,而不仅仅是刮擦表面。

到目前为止我们知道什么

以下是本系列最后几篇文章的回顾:

  • 在JavaScript中,源代码通常要经过几个阶段才能最终执行。这些阶段是标记化,解析和代码生成。
  • 每当运行JavaScript代码时,它都会创建一个称为执行上下文的东西。它有助于确定当前正在运行的代码,并有助于为其途中发现的每个函数和变量声明分配内存。

在执行上下文中为函数和变量声明分配内存的机制称为提升(Hoisting)。

揭开一些神话的神秘面纱

在这里实际发生的情况下,“Hoisting”一词令人困惑和误解。这就是为什么许多有关Hoisting的解释都集中在Hoisting变量和函数这一事实的原因之一。 用通俗的英语来说,“Hoisting”意思是用绳索和滑轮抬起(某种东西)。在某个时候,人们可能会开始相信,通过JavaScript引擎将事物(变量和函数)的位置上移实际上已经将其Hoisting

image-20210404181529424

嘿,放松,什么都没有发生!老实说,没有任何代码会被物理地吊起。这是关于如何在执行上下文的创建阶段为函数,变量声明分配内存的。

我们将在后面的示例中看到这一点。

变量提升(Variable Hoisting)

考虑以下简单代码:

1
2
3
4
console.log('name is ', name);
var name;
name = 'tom';
console.log('name is ', name);

上面的代码的预期输出是什么?好吧,这很容易:

1
2
name is  undefined
name is tom

问题是,为什么?我们早在变量name称被声明之前就已经对其进行了访问。像许多其他编程语言一样,我们应该有一个错误。但是,相反,我们得到undefined

在JavaScript中,代码执行上下文分为两个阶段:

  • 创建阶段
  • 执行阶段

在创建阶段,将为变量分配内存,并使用称为undefined的特殊值进行初始化。

undefined的含义是,已声明变量,但未分配任何值。

在上面的代码示例中,创建阶段通过为其分配内存来声明变量name并将其标记为undefined。这种现象在JavaScript中称为“变量提升”。

在执行阶段的后期,将值tom分配给变量name,并执行控制台日志语句。当创建阶段发生在执行阶段之前时,我们发现变量已经声明,即在内存中创建(就像在提升中一样)。

函数提升(Function Hoisting)

函数提升遵循与可变提升类似的路径。在函数提升中,JavaScript 执行环境的创建阶段将函数声明放入内存中。让我们通过以下示例了解它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Invoke a function, chase()
chase();

// Declare a function, chase()
function chase() {
console.log('Tom chases Jerry!');
// Invoke a function, caught();
caught();
}

// Declare a function, caught()
function caught() {
console.log('Tom caught Jerry :(')
}

执行上下文的创建阶段为函数chase()创建一个内存,并且整个函数声明都已放入该内存中。在执行阶段,可以执行存储器的全部功能。

众所周知,函数创建自己的执行上下文(函数执行环境),其机制在函数执行上下文中保持不变。首先,为catch()创建一个内存,并将声明放入其中。以后执行它。

提升规则(Hoisting Rules)

一般的经验法则是,始终在代码中使用函数,变量等之前定义它们。这可以帮助避免意外情况和调试噩梦。

JavaScript语言中已经存在一些准则和检查,以防止在未意识到的情况下使用Hoisting的安全隐患。

JavaScript仅提升声明,而不初始化。这样,下面的代码将被打破:

1
2
3
4
5
test();

var test = function() {
console.log('I am being tested');
}

由于将使用undefined的值提升和初始化测试声明,因此将引发以下错误。从来没有假设它是一个函数。实际上,它是作为变量提升而不是函数提升来的。

hoisting_error_1.png

也可以提升letconst声明,但是它们不会像var一样初始化为undefined。请参阅此处的示例:

1
2
console.log(foo);
let foo;

这将引发以下错误,但是使用var可以正常运行:

hoisting_error_2.png

总结

如上所述,在代码中使用函数,变量等之前,请务必先定义它们。不要太依赖提升。同时,重要的是要理解为什么某些事物以特定方式表现的基本概念。

后记

翻译自JavaScript Hoisting Internals