在了解什么是 JavaScript 执行上下文之前,我们需要知道如何以及在哪些环境中运行 JavaScript 代码。
首先,我们可以在两种环境中运行JavaScript:
当我们在计算机上编写 JavaScript 代码然后尝试运行它时,代码首先会转到浏览器或 Node.js。
但是,我们编写的 JavaScript 代码并不能被浏览器或 Node.js 直接理解。此时,两者都将代码发送到内置的 JavaScript 引擎。有不同类型的引擎,例如:
接下来,JavaScript 引擎将 JavaScript 代码编译为机器代码。然后,该机器代码被发送到计算机,计算机执行它,我们会看到显示的输出。
作为程序员,我们需要很好地理解这个中间步骤,即 JavaScript 引擎如何将 JavaScript 代码编译为机器代码。
所以,现在我们需要了解 JavaScript 引擎是如何工作的。 JavaScript 引擎以两种方式将代码转换为机器代码。第一个是解释,第二个是编译。那么,什么是解释和编译?
解释是逐行读取所有用高级语言编写的源代码,并在读取后立即将每一行转换为机器代码的过程。如果在读取一行代码时出现错误,该过程会立即停止,从而使程序员可以轻松识别错误。这使得调试变得简单。不过,由于这个过程是逐行读取代码,所以速度相对较慢。
编译是将所有用高级语言编写的源代码一次性转换为机器代码的过程。在这种情况下,即使代码中有错误,它仍然会编译并且只在运行时显示错误。结果,程序员更难识别错误,从而使调试变得更具挑战性。然而,由于整个源代码立即转换为机器代码,因此这个过程相对更快。那么现在问题来了:JavaScript 是编译型语言还是解释型语言?
最初,JavaScript 主要被认为是一种解释性语言。然而,由于这个过程非常缓慢,现代 JavaScript 引擎开始使用一种结合解释和编译的新技术,称为即时 (JIT) 编译。这个过程结合了解释和编译,将代码转换为机器代码。因此,与旧方法相比,它的调试速度更快、更容易。
要了解 JavaScript 的即时 (JIT) 编译如何工作,我们需要了解 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 中有两种类型的执行上下文:
每个执行上下文都会经历两个阶段:创建阶段和执行阶段。
当我们运行 JavaScript 代码时,首先发生的是全局执行上下文。这个上下文首先经历它的创建阶段,其中发生了几件事情:
创建阶段完成后,全局执行上下文将进入下一个阶段:执行阶段,其中会发生更多步骤。
当全局执行上下文的执行阶段引用的函数被调用时,每个函数都会创建自己的函数执行上下文。就像全局执行上下文一样,函数执行上下文也经历创建阶段,其中发生几个步骤:
创建阶段完成后,函数执行上下文将进入执行阶段,其中会发生更多步骤。
当在其他函数中调用函数时,将为每个函数创建一个新的函数执行上下文。然后,每个函数执行上下文都会经历创建阶段和执行阶段。对于在另一个函数内调用的每个函数,此过程都会继续,并且每个函数将分别经历这些阶段。
我们看下图。
我们已经看到全局执行上下文和函数执行上下文都会经历一定的步骤。唯一的区别是,在全局执行上下文中,第一步是创建全局对象,而在函数执行上下文中,第一步是为函数创建参数对象。
现在,问题出现了:当为全局上下文和每个函数创建这些执行上下文时,JavaScript 如何管理这些执行上下文?
为了管理这些上下文,JavaScript 使用一种称为执行堆栈的数据结构。执行堆栈以类似堆栈的方式存储上下文:首先是全局执行上下文,然后是每个函数执行上下文。当所有执行上下文都存储在堆栈中时,JavaScript 从堆栈顶部开始一一处理它们。
需要注意的是,当我们在全局或函数作用域内使用 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中文网其他相关文章!