首页 >web前端 >js教程 >JavaScript 执行上下文 – JS 代码如何在幕后运行

JavaScript 执行上下文 – JS 代码如何在幕后运行

Mary-Kate Olsen
Mary-Kate Olsen原创
2025-01-05 04:47:39283浏览

在了解什么是 JavaScript 执行上下文之前,我们需要知道如何以及在哪些环境中运行 JavaScript 代码。

首先,我们可以在两种环境中运行JavaScript:

  1. 通过浏览器
  2. 通过 Node.js

JavaScript 代码如何在我们的计算机上运行?

当我们在计算机上编写 JavaScript 代码然后尝试运行它时,代码首先会转到浏览器或 Node.js。

但是,我们编写的 JavaScript 代码并不能被浏览器或 Node.js 直接理解。此时,两者都将代码发送到内置的 JavaScript 引擎。有不同类型的引擎,例如:

  1. Google Chrome 中的 V8 引擎,
  2. Mozilla Firefox 中的SpiderMonkey,
  3. Node.js 中的 V8 引擎等

接下来,JavaScript 引擎将 JavaScript 代码编译为机器代码。然后,该机器代码被发送到计算机,计算机执行它,我们会看到显示的输出。

作为程序员,我们需要很好地理解这个中间步骤,即 JavaScript 引擎如何将 JavaScript 代码编译为机器代码。

所以,现在我们需要了解 JavaScript 引擎是如何工作的。 JavaScript 引擎以两种方式将代码转换为机器代码。第一个是解释,第二个是编译。那么,什么是解释和编译?

什么是口译,它是如何运作的?

解释是逐行读取所有用高级语言编写的源代码,并在读取后立即将每一行转换为机器代码的过程。如果在读取一行代码时出现错误,该过程会立即停止,从而使程序员可以轻松识别错误。这使得调试变得简单。不过,由于这个过程是逐行读取代码,所以速度相对较慢。

什么是编译,它是如何工作的?

编译是将所有用高级语言编写的源代码一次性转换为机器代码的过程。在这种情况下,即使代码中有错误,它仍然会编译并且只在运行时显示错误。结果,程序员更难识别错误,从而使调试变得更具挑战性。然而,由于整个源代码立即转换为机器代码,因此这个过程相对更快。那么现在问题来了:JavaScript 是编译型语言还是解释型语言?

JavaScript 是编译型语言还是解释型语言?

最初,JavaScript 主要被认为是一种解释性语言。然而,由于这个过程非常缓慢,现代 JavaScript 引擎开始使用一种结合解释和编译的新技术,称为即时 (JIT) 编译。这个过程结合了解释和编译,将代码转换为机器代码。因此,与旧方法相比,它的调试速度更快、更容易。

要了解 JavaScript 的即时 (JIT) 编译如何工作,我们需要了解 JavaScript 的执行上下文。现在让我们尝试了解 JavaScript 的执行上下文。

JavaScript 执行上下文

首先,看一下下面的代码示例。

代码示例

var a = 1;

function one() {
  console.log(a);

  function two() {
    console.log(b);

    var b = 2;

    function three(c) {
      console.log(a + b + c);
    }

    three(4);
  }

  two();
}

one();

输出

1
undefined
7

当我们运行代码时,我们尝试在two()函数内声明b变量之前打印它,但输出是未定义的。然而,没有发生错误。问题出现了:b 变量的值是如何未定义的?答案就在 JavaScript 执行上下文中。现在,我们将更详细地探讨 JavaScript 执行上下文。

JavaScript 中有两种类型的执行上下文:

  1. 全局执行上下文
  2. 函数执行上下文

每个执行上下文都会经历两个阶段:创建阶段和执行阶段。

全局执行上下文

当我们运行 JavaScript 代码时,首先发生的是全局执行上下文。这个上下文首先经历它的创建阶段,其中发生了几件事情:

创建阶段

  1. 创建了一个全局对象。
  2. 创建此对象并为其分配全局对象的值。
  3. 创建了一个变量对象,其中声明了所有函数和变量。变量被分配为未定义的值,函数被分配对其各自函数的引用。

创建阶段完成后,全局执行上下文将进入下一个阶段:执行阶段,其中会发生更多步骤。

执行阶段

  1. 在创建阶段声明并使用 undefined 初始化的变量现在被分配了各自的值。
  2. 在创建阶段声明的函数(存储为引用)现在被调用并执行。

函数执行上下文

当全局执行上下文的执行阶段引用的函数被调用时,每个函数都会创建自己的函数执行上下文。就像全局执行上下文一样,函数执行上下文也经历创建阶段,其中发生几个步骤:

创建阶段

  1. 为函数创建参数对象。
  2. 创建此对象并为其分配全局对象的值。
  3. 创建了一个变量对象,其中声明了所有函数和变量。变量被分配为未定义的值,函数被分配对其各自函数的引用。

创建阶段完成后,函数执行上下文将进入执行阶段,其中会发生更多步骤。

执行阶段

  1. 在创建阶段声明的变量,之前用 undefined 初始化,现在被分配了各自的值。
  2. 在创建阶段声明的函数现在被调用并执行。

嵌套函数中的函数执行上下文

当在其他函数中调用函数时,将为每个函数创建一个新的函数执行上下文。然后,每个函数执行上下文都会经历创建阶段和执行阶段。对于在另一个函数内调用的每个函数,此过程都会继续,并且每个函数将分别经历这些阶段。

我们看下图。

JavaScript Execution Context – How JS Code Runs Behind the Scenes

我们已经看到全局执行上下文和函数执行上下文都会经历一定的步骤。唯一的区别是,在全局执行上下文中,第一步是创建全局对象,而在函数执行上下文中,第一步是为函数创建参数对象。

现在,问题出现了:当为全局上下文和每个函数创建这些执行上下文时,JavaScript 如何管理这些执行上下文?

使用执行堆栈管理执行上下文

为了管理这些上下文,JavaScript 使用一种称为执行堆栈的数据结构。执行堆栈以类似堆栈的方式存储上下文:首先是全局执行上下文,然后是每个函数执行上下文。当所有执行上下文都存储在堆栈中时,JavaScript 从堆栈顶部开始一一处理它们。

使用 let 和 const 确定作用域

需要注意的是,当我们在全局或函数作用域内使用 let 或 const 声明变量时,这些变量在创建阶段不会存储在变量对象中,也不会使用 undefined 进行初始化。相反,这些变量是在执行阶段直接声明并赋值的。

考虑以下代码示例:

代码示例

var a = 1;

function one() {
  console.log(a);

  function two() {
    console.log(b);

    var b = 2;

    function three(c) {
      console.log(a + b + c);
    }

    three(4);
  }

  two();
}

one();

如果我们运行这段代码,我们将遇到一个ReferenceError。这是因为我们试图在声明 b 变量之前打印它的值,并且由于 b 是使用 const 声明的,因此它的行为与常规变量不同。使用 const 或 let 声明的变量在创建阶段不会存储在变量对象中,这就是为什么在为它们赋值之前尝试访问它们时会出现错误。

结论

我希望对 JavaScript 如何工作以及其执行上下文阶段发生的情况的解释能让您有更清晰的理解。在下一课中,我们将探讨另一个 JavaScript 主题。

您可以在 GitHub 和 Linkedin 上与我联系。

以上是JavaScript 执行上下文 – JS 代码如何在幕后运行的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn