JavaScript - 执行上下文
在本章中,我们将学习 JavaScript 执行上下文,还将涵盖其类型、定义、执行栈、创建过程以及整体执行阶段。我们将逐一讨论每个主题。首先,让我们从介绍开始。
什么是执行上下文?
执行上下文是一个描述代码内部工作机制的术语。JavaScript 执行上下文描述了 JavaScript 代码可以运行的环境。执行上下文指定了代码中使用的函数、变量和对象哪些代码段可以访问。
在执行上下文中,给定的代码会逐行解析,变量和函数保存在内存中。执行上下文类似于存储变量的容器;代码会被求值然后执行。因此,执行上下文提供了一个特定代码可以执行的环境。
执行上下文的类型
JavaScript 执行上下文的类型如下 −
全局执行上下文/GEC
函数执行上下文/FEC
Eval 执行上下文
现在让我们在下面的部分逐一讨论每种类型 −
全局执行上下文
GEC(全局执行上下文)通常被称为基础或默认执行。任何不在函数中的 JavaScript 代码都会在全局执行上下文中找到。“默认执行上下文”一词指的是文件首次加载到 Web 浏览器时代码被执行的事实。GEC 执行以下两个任务 −
首先,为 Node.js 创建全局对象,为浏览器创建 window 对象。
其次,使用关键字 'this' 引用 Windows 对象。
创建内存堆来存储变量和函数引用。
然后将所有函数声明存储在内存堆中,并将 GEC 中的所有变量初始化为 'undefined'。
因为 JS 引擎是单线程的,所以只有一个全局环境可用于执行 JavaScript 代码。
函数执行上下文
FEC(函数执行上下文)是 JavaScript 引擎在发现函数调用时生成的上下文类型。因为每个函数都有自己的执行上下文,与 GEC 不同,FEC 可以有多个实例。而且,FEC 可以访问整个 GEC 代码,而 GEC 无法访问 FEC 的所有代码。在 GEC 代码执行期间,会发起函数调用,当 JS 引擎发现它时,会为该函数生成一个新的 FEC。
Eval 函数执行上下文
使用 eval 函数执行的任何 JavaScript 代码都会创建并保留自己的执行上下文。但 JavaScript 开发者不使用 eval 函数,这是执行上下文的一个组成部分。
JavaScript 执行上下文的阶段
JavaScript 执行上下文有 2 个主要阶段 −
创建阶段: 在创建阶段,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 引擎执行完整的源代码,创建一个全局执行上下文,然后执行以下操作 −
创建一个全局对象,在浏览器中是 window,在 Node.js 中是 global。
为存储变量和函数创建内存。
将变量存储为 undefined 值和函数引用。
创建阶段完成后,执行上下文将移动到代码执行阶段。
执行阶段
在这一步,它开始从上到下逐行运行整个代码。当它找到 n = 5 时,将值 5 分配给内存变量 'n'。最初,'n' 的值默认是 undefined。
然后我们到达 'square' 函数。因为函数已经被分配了内存,它直接跳转到 var sqr1 = square(n); 这一行。然后调用 square(),JavaScript 创建一个新的函数执行上下文。
计算完成后,它将 square 的值分配给之前未定义的 'ans' 变量。函数将返回其值,函数执行上下文将被移除。
square() 生成的值将被分配给 sqr1。这也适用于 sqr2。一旦所有代码执行完毕,全局上下文将如下所示,然后被清除。
执行栈
JavaScript 引擎使用调用栈来跟踪所有上下文,包括全局上下文和函数上下文。调用栈也被称为 Execution Context Stack、Runtime Stack 或 Machine Stack。
它遵循 LIFO 概念(Last-In-First-Out,后进先出)。当引擎开始处理脚本时,它会生成一个全局上下文并将其推入栈中。当调用一个函数时,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("The result is:", res);
输出
这将产生以下结果 −
The result is: 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 引擎逐行遍历代码。这包括评估和执行语句。