context | 上下文
执行上下文是一种对Javascript代码执行环境的抽象概念,也就是说只要有Javascript代码运行,那么它就一定是运行在执行上下文中
类型
- 全局执行上下文:只有一个,浏览器中的全局对象就是
window对象,this指向这个全局对象 - 函数执行上下文:存在无数个,只有在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文
- Eval 函数执行上下文:指的是运行在
eval函数中的代码,很少用而且不建议使用
每次调用函数创建一个新的上下文,会创建一个私有作用域,函数内部声明的任何变量都不能在当前函数作用域外部直接访问,只有全局上下文(的变量)能被其他任何上下文访问。
生命周期
创建阶段 → 执行阶段 → 回收阶段
创建阶段
即当函数被调用,但未执行任何其内部代码之前
- 确定
this的值,也被称为 This Binding - 创建词法环境,也被称为 Lexical Environment
- 创建变量环境,也被称为 Variable Environment
伪代码:
ExecutionContext = {
ThisBinding: <this value>, // this 值
LexicalEnvironment: { // 词法环境
// identifier bindings go here
},
VariableEnvironment: { // 变量环境
// identifier bindings go here
}
}
This Binding
官方的称呼为This Binding,在全局执行上下文中, this 总是指向全局对象,例如浏览器环境下 this 指向 window 对象。
而在函数执行上下文中,this 的值取决于函数的调用方式,如果被一个对象调用,那么 this 指向这个对象。否则this一般指向全局对象 window 或者 undefined (严格模式)。
Lexical Environment
词法环境是一个规范类型,用于定义标识符和具体变量和函数之间的关系,也就是说词法环境是一个存储标识符与变量和函数之间关系的数据结构
词法环境分为两种类型:
- 全局环境:对外部环境的引入记录为null,因为它本身就是最外层环境,除此之外它还记录了当前环境下的所有属性、方法位置。
- 函数环境:包含了用户在函数中定义的所有属性方法外,还包含了一个arguments对象。函数词法环境的外部环境引入可以是全局环境,也可以是其它函数环境,这个根据实际代码而来。
译文伪代码:
// 全局环境
GlobalExectionContext = {
// 全局词法环境
LexicalEnvironment: {
// 环境记录
EnvironmentRecord: {
Type: "Object", //类型为对象环境记录
// 标识符绑定在这里
},
outer: < null >
}
};
// 函数环境
FunctionExectionContext = {
// 函数词法环境
LexicalEnvironment: {
// 环境纪录
EnvironmentRecord: {
Type: "Declarative", //类型为声明性环境记录
// 标识符绑定在这里
},
outer: < Global or outerfunction environment reference >
}
};
Variable Environment
变量环境可以说也是词法环境,它具备词法环境所有属性,一样有环境记录与外部环境引入。在ES6中唯一的区别在于词法环境用于存储函数声明与let、const声明的变量,而变量环境仅仅存储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 = {
// this绑定为全局对象
ThisBinding: <Global Object>,
// 词法环境
LexicalEnvironment: {
//环境记录
EnvironmentRecord: {
Type: "Object", // 对象环境记录
// 标识符绑定在这里 let const创建的变量a b在这
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
}
// 全局环境外部环境引入为null
outer: <null>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object", // 对象环境记录
// 标识符绑定在这里 var创建的c在这
c: undefined,
}
// 全局环境外部环境引入为null
outer: <null>
}
}
// 函数执行上下文
FunctionExectionContext = {
//由于函数是默认调用 this绑定同样是全局对象
ThisBinding: <Global Object>,
// 词法环境
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative", // 声明性环境记录
// 标识符绑定在这里 arguments对象在这
Arguments: {0: 20, 1: 30, length: 2},
},
// 外部环境引入记录为</Global>
outer: <GlobalEnvironment>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative", // 声明性环境记录
// 标识符绑定在这里 var创建的g在这
g: undefined
},
// 外部环境引入记录为</Global>
outer: <GlobalEnvironment>
}
}
创建阶段,在代码中扫描变量和函数声明,将函数声明存储在环境中,但变量会被初始化为 undefined ( var 声明的情况下)和保持 < uninitialized >(使用 let 和 const 声明的情况下)
这就是变量提升的实际原因
执行阶段
在这阶段,执行变量赋值、代码执行
如果 Javascript 引擎在源代码中声明的实际位置找不到变量的值,那么将为其分配 undefined 值
回收阶段
当函数执行完毕,或者全局上下文中的代码执行完毕,那么执行上下文就会被销毁,这个过程称为执行上下文的回收阶段
执行栈
执行栈是一种拥有 LIFO(后进先出)数据结构的栈,用于存储代码运行时创建的所有执行上下文
