1. 什么是执行上下文?
简而言之,每当JavaScript代码在运行时,都会做一个准备工作,这个准备工作就是执行上下文。
2. 执行上下文的类型
- 全局执行上下文
这是默认的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:1. 创建一个全局对象,2.设置
this的值等于这个全局对象。 - 函数执行上下文 每当一个函数调用时,都会创建一个上下文,按照顺序执行。
- Eval函数执行上下文 不常用
3. 执行栈
当Js引擎第一次遇见你的脚本时,它会创建一个全局的执行上下文压入当前的执行栈中,每当引擎遇到一个函数调用,即将一个函数执行上下文压入栈。当函数执行结束后,则弹出。看如下示例
let 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');
4. 创建执行上下文
在创建阶段有三件事:
- This绑定
- 创建词法环境组件
- 创建环境变量组件
1. This绑定
- 全局执行上下文:
this指向全局对象。 - 函数执行上下文:
this指向被调用的对象。如果没被调用,则指向全局。
2. 创建词法环境
1. 什么是词法环境?
词法环境就是持有 标识符-变量映射的结构。(名-值)
2. 词法环境的内部组件
- 环境记录器:存储变量/函数的声明位置
- 外部环境引用: 可以访问父级词法环境(作用域)
3. 词法环境的类型
全局环境包括
- 内建的Object/Array
- 在(对象)环境记录器中的原型函数和自定义的全局变量。 外部词法环境为null
函数环境包括
- 在(声明式)环境记录器中的函数内部自定义的变量。
- 声明式环境记录器中还包含了一个传递函数的
arguments对象和length属性。 外部词法环境为全局/包含此函数的函数。
伪代码如下:
全局执行上下文 = {
词法环境: {
对象环境记录器: {
Type: "Object",
// 在这里绑定标识符
}
outer: <null>
}
}
函数执行上下文 = {
词法环境: {
声明式环境记录器: {
Type: "Declarative",
// 在这里绑定标识符
}
outer: <Global or outer function environment reference>
}
}
3. 创建变量环境
变量环境其实也是一个词法环境,在ES6中的一个不同就是变量环境存储var变量绑定的数据。观察下面代码来理解如上概念。
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
它的执行上下文如下:
GlobalExectionContext = {
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>
}
}
我们可以看到用let和const定义的变量没有关联值,而var定义的变量设置成了undefined。这就是var为什么有变量提升。
5. 执行
完成对所有变量的分配,执行代码。