什么是执行上下文?
执行上下文是分析和执行实际 JavaScript 代码的环境的抽象概念。
执行上下文的重要性在于,它提供了一个抽象模型,可以让我们更容易理解js的运行机制。同时,执行上下文对于理解js内存、闭包、垃圾回收等都有着深刻的意义。它允许我们在不太了解底层代码的情况下分析内存和执行过程。
执行上下文可以分为三类:
- 全局执行上下文:只有一个,由客户端浏览器创建,通常是一个窗口;
- 函数执行上下文:可以有无数个,只有在函数调用时才会创建,每次调用函数时都会创建一个新的执行上下文;
- eval执行上下文:指eval中的代码,一般不推荐使用。
许多执行上下文可以使用执行堆栈进行管理。
执行堆栈
执行堆栈,也称为调用堆栈,是一种后进先出的结构,用于存储代码在执行过程中所需的上下文。
代码第一次执行时,全局执行上下文被放置在栈底,然后根据函数调用将函数执行上下文移动到栈顶。当栈顶的函数终止时,其对应的函数执行上下文将从执行栈中弹出,上下文控制权将转移到当前执行栈的下一个执行上下文。
var a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');
// Inside first function
// Inside second function
// Again inside first function
// Inside Global Execution Context
创建执行上下文
执行上下文的创建分为三个阶段:
- 确保它指向
- 创建词汇环境
- 创建可变环境
确保它指向
- 在全局上下文中,它指向全局对象。在浏览器中,它指向一个窗口对象。在nodejs中它指向该文件所在的模块;
- 在函数的上下文中,这取决于函数的调用方式。
创建词汇环境
词汇环境由两部分组成:
- 环境记录:保存定义的变量和函数;
- 链接到外部环境:可访问的外部词汇环境
词汇环境可分为:
- 全局环境:这是一个没有外部环境的词法环境,因此对外部环境的引用为空。它有一个全局对象(窗口)和关联的属性和方法,以及用户定义的全局变量;
- 功能环境:外部环境的参考可以是全局环境,也可以是功能环境。函数中的用户定义变量存储在环境记录中。
可以看下面的伪代码,更直观:
GobelExectionContext = { // 全局执行上下文
LexicalEnvironment: { // 词法环境
EnvironmentRecord: { // 环境记录
Type: "Object", // 对象环境记录
......
},
outer: <null> // 对外部环境的引用
}
}
FunctionExectionContext = { // 函数执行上下文
LexicalEnvironment: { // 词法环境
EnvironmentRecord: { // 环境记录
Type: "Declarative", // 声明性环境记录
......
},
outer: <GobelEvironment or FunctionEvironment> // 对外部环境的引用
}
}
创造一个可变的环境
环境变量也是一个词法环境,因此它具有前面提到的词法环境的属性。
在es6中,变量环境和词法环境的区别在于前者用于存储用var定义的变量,后者用于存储声明的函数和变量(let、const)。
您可以看到以下示例:
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
执行上下文如下:
GobelExectionContext = { // 全局执行上下文
LexicalEnvironment: { // 词法环境
EnvironmentRecord: { // 环境记录
Type: "Object", // 对象环境记录
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
},
outer: <null> // 对外部环境的引用
}
VariableEnvironment: { // 变量环境
EnvironmentRecord: {
Type: "Object", // 对象环境记录
c: undefined,
}
outer: <null>
}
}
FunctionExectionContext = { // 函数执行上下文
LexicalEnvironment: { // 词法环境
EnvironmentRecord: { // 环境记录
Type: "Declarative", // 声明性环境记录
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GobelLexicalEvironment>
}
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative", // 声明性环境记录
g: undefined,
}
outer: <GobelLexicalEvironment>
}
}
这里可以看到支持变量的原因:变量在环境变量中以未定义的方式存储(用var声明);变量以未初始化的形式存储在词法环境中(使用 let 和 const 声明),因此在使用 let 和 const 声明之前使用变量将导致错误。
执行上下文的执行
到了这个阶段,对变量的赋值就完成了,即上面的执行上下文变成了:
GobelExectionContext = { // 全局执行上下文
LexicalEnvironment: { // 词法环境
EnvironmentRecord: { // 环境记录
Type: "Object", // 对象环境记录
a: 20,
b: 30,
multiply: < func >
},
outer: <null> // 对外部环境的引用
}
VariableEnvironment: { // 变量环境
EnvironmentRecord: {
Type: "Object", // 对象环境记录
c: <multiply>,
}
outer: <null>
}
}
FunctionExectionContext = { // 函数执行上下文
LexicalEnvironment: { // 词法环境
EnvironmentRecord: { // 环境记录
Type: "Declarative", // 声明性环境记录
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GobelLexicalEvironment>
}
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative", // 声明性环境记录
g: 20,
}
outer: <GobelLexicalEvironment>
}
}
以上内容为作者个人理解。如果有任何错误,请报告!
参考:
了解JS执行上下文的文章
了解 JavaScript 中的执行上下文和执行堆栈
js没那么简单(一)——执行上下文
已预编译
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。