先来回顾一下ES3的执行上下文
ES3执行上下文包含
1.作用域链
2.环境变量
3.this
下面来介绍ES6的执行上下文的结构
我们将作用域链的功能以及环境变量的功能整合,也就是ES6中的词法环境
词法环境包含环境记录器以及外部环境的引用
环境记录器也就是记录了当前环境的变量和函数申明,如果该上下文为函数上下文,那么环境记录器中需要记录arguments(函数实参),全局上下文没有传参自然不需要记录
在全局环境中,环境记录器是对象环境记录器。 在函数环境中,环境记录器是声明式环境记录器。
外部环境的引用相当于作用域链的作用,意味着它可以访问其父级词法环境
词法环境变量的结构如下图所示
而在ES6中,加入了let 和 const,词法环境只满足var,用let和const定义的变量我们将其放入变量环境中,这就是ES6执行上下文的第二个元素,他与词法变量的结构相同
最后一个依然是this
所以组成执行上下文的三个部分分别是词法环境,变量环境,this
下面看一个直观的例子
代码
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
上下文
// 首先进入全局上下文
GlobalExectionContext = {
// this
ThisBinding: <Global Object>,
// 全局上下文的词法变量
LexicalEnvironment: {
// 环境记录器
EnvironmentRecord: {
Type: "Object",
// 在这里绑定标识符
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >,
}
// 外部环境的引用
outer: <null>
},
// 变量环境
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 在这里绑定标识符
c: undefined,
}
outer: <null>
}
}
// 这里是函数的上下文
FunctionExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 在这里绑定标识符
Arguments: {0: 20, 1: 30, length: 2},
},
// 外部环境的引用,指向其父级词法环境
outer: <GlobalLexicalEnvironment>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 在这里绑定标识符
g: undefined
},
outer: <GlobalLexicalEnvironment>
}
}
补充:JS编译与执行
JS执行过程可以分为三步: 语法分析--->预编译阶段--->执行阶段
- 语法分析:顾名思义进行语法分析,有语法错误会直接抛出并停止执行该代码块
- 预编译阶段:创建执行上下文的阶段,如上述代码中模拟出的执行上下文的样子
- 执行阶段:在执行上下文创建好之后,开始逐步执行代码,执行机制也就是事件循环。执行函数时,又会进入到预编译与阶段,创建函数执行上下文,然后再执行
对于JS执行来说,是单线程,我们将其称为IS引擎线程,但是浏览器是多线程,例如事件触发线程,定时器触发线程等,这类线程会将符合条件的事件或函数添加到回调队列中,等待JS引擎线程调用。
参考文章:[译] 理解 JavaScript 中的执行上下文和执行栈 - 掘金 (juejin.cn)
本文的代码及某些定义/概念均引用自此文