
概述
执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念
执行栈
也叫调用栈,具有 IFLO(先进后出)的结构,用于存储代码执行期间创建的所有执行上下文
当 JS 引擎 首次读取 js 代码时,会首先创建一个全局执行上下文压入执行栈中,当发生函数调用时,又会为函数创建一个函数执行栈压入执行栈中,当该函数执行完成,就会从执行栈中弹出
类型
全局执行上下文
默认的、最基础的执行上下文,不在任何函数中代码都位于全局执行上下文中
创建过程:
- 创建一个全局对象,在浏览器中这个全局对象就是 window 对象
- 将
this指向这个全局对象
一个程序只能存在一个全局执行上下文
函数执行上下文
每次 调用 函数时,才会为该函数创建一个函数执行上下文。每个新的函数执行上下文被创建,都会按照 执行栈 的顺序进行执行
一个程序可以存在多个函数执行上下文
Eval 函数执行上下文
运行在 eval 函数中的代码也获得了自己的执行上下文
执行栈举例
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');执行栈中的流程图:
创建过程
执行上下文的创建分为两个阶段:1.初始化阶段 2.执行阶段
初始化阶段
在这一阶段,需要做三件事情:
- 确定 this 的值,也就是
This Binding - 创建
LexicalEnvironment(词法环境 - 创建
VariableEnvironment(变量环境
This Binding
全局执行上下文中, this 指向全局对象——window 对象
函数执行上下文中,this 指向调用者
词法环境
- 环境记录:存储变量和函数声明的实际位置
- 对外部环境的引用:意味着可以访问其外部词法环境
在函数执行上下文中,环境记录还包含了一个 arguments 对象,表示函数参数的相关属性
function foo(a, b) {
var c = a + b;
}
foo(2, 3);
// arguments 对象
Arguments: {0: 2, 1: 3, length: 2},在全局执行上下文中,对外部环境的引用为 null
变量环境
它也是一个词法环境,其 EnvironmentRecord 包含了由 VariableStatements 在此执行上下文创建的绑定
如上所述,变量环境也是一个词法环境,因此它具有上面定义的词法环境的所有属性。
在 ES6 中,LexicalEnvironment 组件和 VariableEnvironment 组件的区别在于前者用于存储函数声明和变量( 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 = {
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>
}
}执行阶段
在此阶段,完成对所有变量的分配,最后执行代码
执行上下文与作用域的区别
作用域是在函数声明时就确定的一套变量的访问规则,而执行上下文是函数执行时才产生的一系列变量的环境。
也就是说,作用域定义了执行上下文中的变量的访问规则,执行上下文在这个作用域规则的前提下进行变量查找,函数引用等具体操作