JavaScript - 执行上下文
本章将学习 JavaScript 执行上下文,涵盖其类型、定义、执行堆栈、创建过程以及整个执行阶段。我们将逐一讲解每个主题。首先,我们来了解一下简介。
什么是执行上下文?
执行上下文是一个描述代码内部工作原理的术语。JavaScript 执行上下文描述了 JavaScript 代码的运行环境。执行上下文指定哪些代码段可以访问代码中使用的函数、变量和对象。
在执行上下文中,给定的代码会被逐行解析,变量和函数会被保存在内存中。执行上下文类似于存储变量的容器;代码会被求值并执行。因此,执行上下文提供了一个可以执行特定代码的环境。
执行上下文的类型
JavaScript 执行上下文的类型如下:-
全局执行上下文/GEC
函数执行上下文/FEC
求值执行上下文
现在,我们将在下一节中逐一讨论每种类型:-
全局执行上下文
GEC(全局执行上下文)通常被称为基本执行上下文或默认执行上下文。任何未在函数中出现的 JavaScript 代码都会在全局执行上下文中找到。"默认执行上下文"指的是文件首次加载到 Web 浏览器时执行的代码。 GEC 执行以下两项任务 -
首先,它为 Node.js 创建一个全局对象,为浏览器创建一个窗口对象。
其次,使用关键字"this"引用窗口对象。
创建一个内存堆来存储变量和函数引用。
然后,它将所有函数声明存储在内存堆中,并使用"undefined"初始化 GEC 中的所有变量。
由于 JS 引擎是单线程的,因此只有一个全局环境可用于执行 JavaScript 代码。
函数执行上下文
FEC(函数执行代码)是 JavaScript 引擎在发现函数调用时生成的上下文类型。由于每个函数都有自己的执行上下文,因此 FEC 与 GEC 不同,可以拥有多个实例。此外,FEC 可以访问整个 GEC 代码,而 GEC 无法访问 FEC 的所有代码。在 GEC 代码执行期间,会发起一个函数调用,当 JS 引擎找到该函数时,它会为该函数生成一个新的 FEC。
eval 函数执行上下文
任何使用 eval 函数执行的 JavaScript 代码都会创建并保留其自己的执行上下文。但是 JavaScript 开发者不会使用作为执行上下文组成部分的 eval 函数。
JS 中执行上下文的阶段
JavaScript 执行上下文主要分为两个阶段 -
创建阶段:在创建阶段,JavaScript 引擎会建立执行上下文并配置脚本的环境。它设置变量和函数的值以及执行上下文的作用域链。
执行阶段:在此阶段,JavaScript 引擎在执行上下文中运行代码。它会解析脚本中的所有语句或表达式,并执行所有函数调用。
JavaScript 中的所有内容都在执行上下文中运行。它分为两部分:内存和代码。需要注意的是,这些阶段和组成部分适用于全局执行设置和函数执行设置。
创建阶段
让我们看下面的示例 -
var n = 6;
function square(n) {
var ans = n * n;
return ans;
}
var sqr1 = square(n);
var sqr2 = square(8);
console.log(sqr1)
console.log(sqr2)
输出
这将生成以下结果 -
36 64
首先,JavaScript 引擎会执行完整的源代码,创建一个全局执行上下文,然后执行以下操作:
创建一个全局对象,该对象在浏览器中是一个窗口,在 Node.js 中是全局变量。
创建一个用于存储变量和函数的内存。
存储未定义值的变量和函数引用。
创建阶段结束后,执行上下文将进入代码执行阶段。
执行阶段
在此步骤中,它开始从上到下逐行运行整个代码。当它发现 n = 5 时,它将值 5 赋给内存变量 'n'。最初,'n' 的值默认为未定义。
然后我们进入 'square' 函数。由于函数已分配内存,因此它直接执行到 var square1 = square(n); 这行。然后调用 square(),JavaScript 会创建一个新的函数执行上下文。
计算完成后,它会将 square 的值赋给之前未定义的变量 'ans'。函数将返回该值,并移除函数执行上下文。
square() 生成的值将赋给 square1。这同样适用于 square two。所有代码执行完成后,全局上下文将如下所示,并被清除。
执行堆栈
JavaScript 引擎使用调用堆栈来跟踪所有上下文,包括全局上下文和函数上下文。调用堆栈也称为执行上下文堆栈、运行时堆栈或机器堆栈。
它遵循 LIFO(后进先出)的概念。当引擎最初开始处理脚本时,它会生成一个全局上下文并将其推送到堆栈。当一个函数被调用时,JS 引擎会构建一个函数堆栈上下文,将其移至调用堆栈的顶部并开始执行。
当前函数执行完成后,JavaScript 引擎会从调用堆栈中移除该上下文并将其返回给其父级。我们来看看下面的示例代码:
function firstFunc(m,n) {
return m * n;
}
function secondFunc(m,n) {
return firstFunc(m,n);
}
function getResult(num1, num2) {
return secondFunc(num1, num2)
}
var res = getResult(6,7);
console.log("结果为:", res);
输出
这将产生以下结果 -
结果为:42
在这种情况下,JS 引擎会创建一个全局执行上下文并启动创建过程。
它首先为 firstFunc、secondFunc、getResult 函数和 res 变量分配内存。然后调用 getResult(),该函数会被推送到调用栈。
然后 getResult() 调用 secondFunc()。此时,secondFunc 的上下文将被保存到调用栈顶部。然后它将开始执行并调用另一个函数 firstFunc()。类似地,函数 A 的上下文也会被推送到调用栈。
每个函数执行完成后,都会从调用栈中移除。
调用堆栈的大小由操作系统或浏览器决定。如果上下文数量超出限制,则会返回堆栈溢出错误。当递归函数包含基本条件时,就会发生这种情况。
function display() {
display();
}
display();
输出
这将生成以下结果 -
C:\Users\abc\Desktop\Javascript\example.js:2
display();
^
RangeError: Maximum call stack size exceeded
总结
最后,要理解 JavaScript 的幕后工作原理,需要了解执行上下文。它定义了代码的执行环境以及可用的变量和函数。
构建方法包括创建全局执行上下文和函数执行上下文、作用域链以及为变量和函数分配内存。在执行阶段,JavaScript 引擎会逐行检查代码,包括评估和执行语句。

