Skip to content

执行上下文

什么是执行上下文

  • 执行上下文(Execution Context)它描述了代码在运行时的环境。每当JavaScript代码执行时,都会创建一个执行上下文。
  • 执行上下文可以分为三类:全局执行上下文、函数执行上下文和eval执行上下文。

全局执行上下文(Global Execution Conte)

  • 这是默认的、最基础的执行上下文。代码在浏览器中运行时,默认的执行上下文是全局执行上下文。
  • 全局执行上下文会创建一个全局对象(在浏览器中是 window 对象)和一个特殊的变量 this,它指向全局对象。

函数执行上下文(Function Execution Conte)

  • 每当一个函数被调用时,都会为该函数创建一个新的执行上下文。
  • 每个函数都有自己的执行上下文,但只有在函数被调用时才会被创建。

eval执行上下文(Eval Execution Conte)

  • 代码在 eval 函数中执行时,会有自己的执行上下文。(不做关注)

执行上下文的创建

执行上下文在创建阶段中总共发生了三件事情:

  1. 确定 this 的值,也被称为 This 绑定。
  2. LexicalEnvironment(词法环境) 组件被创建。
  3. VariableEnvironment(变量环境) 组件被创建。

执行上下文This 绑定

  • 在全局执行上下文中,this 的值指向全局对象,在浏览器中,this 的值指向 window 对象。
  • 在函数执行上下文中,this 的值取决于函数的调用方式。如果它被一个对象引用调用,那么 this 的值被设置为该对象,否则 this 的值被设置为全局对象或 undefined(严格模式下)

词法环境

官方 ES6 文档将词法环境定义为:

词法环境是一种规范类型,基于 ECMAScript 代码的词法嵌套结构来定义标识符与特定变量和函数的关联关系。词法环境由环境记录(environment record)和对外环境引用(outer)组成。

环境记录 与 对外环境引用

环境记录是存储变量和函数声明的实际位置。它有两种类型。

  • 声明性环境记录:存储变量、函数和参数。一个函数环境包含声明性环境记录。
  • 对象环境记录:用于定义在全局执行上下文中出现的变量和函数的关联。全局环境包含对象环境记录。

对外环境引用意味着它可以访问其外部词法环境

词法环境有两种类型:

  • 全局环境:在全局执行上下文中没有外部环境的词法环境(为null)。它拥有一个全局对象(window 对象)及其关联的方法和属性(例如数组方法)以及任何用户自定义的全局变量,this 的值指向这个全局对象。
  • 函数环境:在函数中定义的变量被存储在环境记录中。对外部环境的引用可以是全局环境,也可以是包含内部函数的外部函数环境。

    对于函数环境而言,环境记录 还包含了一个 arguments 对象,该对象包含了索引和传递给函数的参数之间的映射以及传递给函数的参数的长度(数量)

变量环境

  • 它也是一个词法环境,因此它具有上面定义的词法环境的所有属性。其 EnvironmentRecord 包含了由 VariableStatements 在此执行上下文创建的绑定。
  • 在 ES6 中,LexicalEnvironment 组件和 VariableEnvironment 组件的区别在于前者用于存储函数声明和变量( let 和 const )绑定,而后者仅用于存储变量( var )绑定。

执行上下文生命周期

创建-->执行-->销毁

  1. 创建阶段
    • 作用域链(Scope Chain):作用域链是一个列表,它包含了当前执行上下文的变量对象和所有父级执行上下文的变量对象。作用域链确保了当前执行上下文可以访问到所有在其作用域链上的变量和函数。
    • 变量对象(Variable Object, VO):
      • 函数的参数:如果是函数执行上下文,变量对象会包含函数的参数。
      • 函数声明:所有的函数声明都会被提升到变量对象的顶部(提升的优先级:函数高于变量)。
      • 变量声明:所有的变量声明都会被提升到变量对象的顶部,但不会赋值。
    • this绑定:在执行上下文中,this的值取决于函数的调用方式:
      • 在全局执行上下文中,this指向全局对象。
      • 在函数执行上下文中,this的值取决于函数的调用方式(例如,作为对象的方法调用时,this指向该对象)。

  1. 执行阶段创建阶段会根据一定的规则创建一个有序执行栈(这个规则:词法环境),代码会被逐行执行,变量和函数会被赋值,代码逻辑会被执行。

  1. 销毁阶段:执行上下文被销毁,内存被释放。

代码示例

javascript
var a = 10;
function foo() {
    var b = 20;
    console.log(a + b);
}
foo();
var a = 10;
function foo() {
    var b = 20;
    console.log(a + b);
}
foo();

1.全局执行上下文:

  • 创建阶段:
    • 变量对象:{ a: undefined, foo: <function> }
    • 作用域链:[全局变量对象]
    • this:指向全局对象
  • 执行阶段:
    • 变量 a 被赋值为 10
    • 函数 foo 被定义
  1. 函数执行上下文(调用 foo 时):
    • 创建阶段:
      • 变量对象:
      • 作用域链:[函数变量对象, 全局变量对象]
      • this:取决于调用方式(在这里是全局对象)
  2. 执行阶段:
    • 变量 b 被赋值为 20
    • 执行 console.log(a + b),输出 30

执行栈

在 JavaScript 中,执行上下文的执行栈(Execution Stack),也称为调用栈(Call Stack),是一个用于管理执行上下文的栈结构。它遵循后进先出(LIFO, Last In First Out)的原则,用于跟踪函数调用和执行顺序。

执行栈的工作流程

  1. 全局执行上下文:
    • 当 JavaScript 引擎开始执行代码时,会首先创建一个全局执行上下文,并将其推入执行栈的底部。
    • 全局执行上下文在整个应用程序的生命周期内始终存在。
  2. 函数执行上下文:
    • 每当一个函数被调用时,JavaScript 引擎会为该函数创建一个新的执行上下文,并将其推入执行栈的顶部。
    • 当函数执行完毕后,JavaScript 引擎会将该函数的执行上下文从执行栈中弹出,控制权返回到栈中的下一个执行上下文。

代码示例

javascript
function firstFunction() {
    console.log("Inside firstFunction");
    secondFunction();
    console.log("Exiting firstFunction");
}
function secondFunction() {
    console.log("Inside secondFunction");
    thirdFunction();
    console.log("Exiting secondFunction");
}
function thirdFunction() {
    console.log("Inside thirdFunction");
}

console.log("Starting execution");
firstFunction();
console.log("Ending execution");
function firstFunction() {
    console.log("Inside firstFunction");
    secondFunction();
    console.log("Exiting firstFunction");
}
function secondFunction() {
    console.log("Inside secondFunction");
    thirdFunction();
    console.log("Exiting secondFunction");
}
function thirdFunction() {
    console.log("Inside thirdFunction");
}

console.log("Starting execution");
firstFunction();
console.log("Ending execution");

执行栈执行过程

  1. 初始状态:
    • 执行栈:[]
    • 全局执行上下文被创建并推入执行栈。
    • 执行栈:[Global Execution Context]
  2. 执行 console.log("Starting execution")
    • 输出:Starting execution
    • 执行栈:[Global Execution Context]
  3. 调用 firstFunction()
    • firstFunction 的执行上下文被创建并推入执行栈。
    • 执行栈:[Global Execution Context, firstFunction Execution Context]
    • 输出:Inside firstFunction
  4. 调用 secondFunction()
    • secondFunction 的执行上下文被创建并推入执行栈。
    • 执行栈:[Global Execution Context, firstFunction Execution Context, secondFunction Execution Context]
    • 输出:Inside secondFunction
  5. 调用 thirdFunction()
    • thirdFunction 的执行上下文被创建并推入执行栈。
    • 执行栈:[Global Execution Context, firstFunction Execution Context, secondFunction Execution Context, thirdFunction Execution Context]
    • 输出:Inside thirdFunction
    • thirdFunction 执行完毕后,其执行上下文从执行栈中弹出。
    • 执行栈:[Global Execution Context, firstFunction Execution Context, secondFunction Execution Context]
  6. 返回到 secondFunction
    • 输出:Exiting secondFunction
    • secondFunction 执行完毕后,其执行上下文从执行栈中弹出。
    • 执行栈:[Global Execution Context, firstFunction Execution Context]
  7. 返回到 firstFunction
    • 输出:Exiting firstFunction
    • firstFunction 执行完毕后,其执行上下文从执行栈中弹出。
    • 执行栈:[Global Execution Context]
  8. 执行 console.log("Ending execution")
    • 输出:Ending execution
    • 执行栈:[Global Execution Context]