【js篇】深入理解 JavaScript 执行上下文

30 阅读5分钟

在 JavaScript 中,执行上下文(Execution Context) 是代码执行的“运行环境”。它决定了变量如何查找、this 如何绑定、函数如何调用。理解执行上下文,是掌握 JS 引擎工作原理、闭包、作用域链、this 指向等核心概念的关键。

本文将系统性地解析 执行上下文的类型、执行栈、创建过程(词法环境与变量环境),帮你构建完整的 JS 执行模型。


一、什么是执行上下文?

执行上下文是 JavaScript 引擎为当前正在执行的代码块创建的一个抽象环境,它包含了代码执行所需的所有信息。

你可以把它理解为:

“一个函数或一段代码在运行时的‘工作台’,上面摆放着它的变量、函数、this 等资源。”


二、执行上下文的三种类型

✅ 1)全局执行上下文(Global Execution Context)

  • 触发时机:JavaScript 程序启动时自动创建;
  • 数量:整个程序只有一个全局上下文;
  • 创建内容
    • 创建全局对象(浏览器中是 window);
    • this 绑定到全局对象;
    • 解析全局代码,进行变量提升函数提升
// 全局上下文
let globalVar = 'I am global';
function globalFunc() { }

✅ 2)函数执行上下文(Function Execution Context)

  • 触发时机每次函数被调用时都会创建一个新的函数上下文;
  • 数量:可以有任意多个,取决于函数调用次数;
  • 创建内容
    • 创建 arguments 对象(非箭头函数);
    • 确定 this 的值;
    • 解析函数内部的变量和函数声明。
function foo() {
  // 每次调用 foo() 都会创建一个新的函数执行上下文
  let localVar = 'I am local';
}
foo(); // 第1个上下文
foo(); // 第2个上下文

✅ 3)eval 执行上下文(Eval Execution Context)

  • eval() 函数中执行的代码会创建自己的执行上下文;
  • 由于 eval 存在安全风险且性能较差,不推荐使用,此处不做深入讨论。

三、执行上下文栈:代码执行的“调用顺序”

✅ 什么是执行上下文栈(Call Stack)?

JavaScript 引擎使用一个栈结构(后进先出,LIFO)来管理执行上下文的创建和销毁。

🔧 工作流程:

  1. 程序启动 → 创建全局执行上下文 → 压入栈底;
  2. 遇到函数调用 → 创建函数执行上下文 → 压入栈顶;
  3. 执行栈顶的上下文;
  4. 函数执行完毕 → 从栈中弹出该上下文;
  5. 继续执行下一个上下文;
  6. 所有代码执行完毕 → 弹出全局上下文,程序结束。

🔍 示例分析

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();

执行过程与栈的变化:

步骤栈状态(从底到顶)当前执行
1[全局]全局代码
2[全局, first]first() 开始
3[全局, first, second]second() 开始
4[全局, first]second() 结束
5[全局]first() 结束
6[]程序结束

📌 输出顺序

Inside first function
Inside second function
Again inside first function

❌ 你提到的“先执行 second,再执行 first”是不准确的。实际上是 first 先开始执行,然后调用 secondsecond 执行完后继续执行 first 的剩余代码。


四、执行上下文的创建阶段:预解析的秘密

创建执行上下文分为两个阶段:

✅ 阶段1:创建阶段(Creation Phase)

在代码执行前,JS 引擎会进行“预解析”,创建上下文的结构。

(1)this 绑定

上下文类型this 指向
全局上下文window(浏览器)或 global(Node.js)
函数上下文取决于调用方式
obj.func()obj
func()window(非严格模式)或 undefined(严格模式)

(2)创建词法环境(Lexical Environment)

  • 定义:用于存储函数声明块级作用域变量let/const)的映射;
  • 结构
    • 环境记录器(Environment Record):存储变量和函数声明;
    • 外部环境引用(Outer Environment Reference):指向外层作用域,形成作用域链。

(3)创建变量环境(Variable Environment)

  • 定义:用于存储 var 声明的变量
  • 本质:也是一个词法环境,但在 ES6 中被分离出来,主要用于处理 var 的变量提升。

📌 简单理解

  • 词法环境:管理 let/const函数声明;
  • 变量环境:管理 var 声明;
  • 两者都参与作用域链的构建。

✅ 阶段2:执行阶段(Execution Phase)

  • 变量赋值(将 var 变量从 undefined 赋为实际值);
  • 函数执行;
  • 逐行执行代码。

五、图解:执行上下文的内部结构

函数执行上下文 {
  LexicalEnvironment: {
    EnvironmentRecord: {
      a: <uninitialized>,  // let/const 声明(暂时性死区)
      func: function() {}
    },
    Outer: <全局词法环境>
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      b: undefined          // var 声明(变量提升)
    },
    Outer: <全局词法环境>
  },
  ThisBinding: window       // this 的值
}

六、经典案例:变量提升与执行顺序

console.log(a); // undefined(来自变量环境)
console.log(b); // 报错!Cannot access 'b' before initialization(暂时性死区)

var a = 1;
let b = 2;

📌 原因

  • var a变量环境中被提升,初始值为 undefined
  • let b词法环境中被声明,但未初始化,访问会报错。

七、总结:执行上下文核心要点

概念关键点
全局上下文唯一,程序启动时创建,this 指向 window
函数上下文每次调用创建,包含 thisarguments
执行栈后进先出,管理上下文的执行顺序
创建阶段this 绑定 + 词法环境 + 变量环境
词法环境管理 let/const/函数声明
变量环境管理 var 声明(变量提升)
执行阶段变量赋值,代码执行

💡 结语

执行上下文是 JavaScript 的‘运行时宇宙’,所有代码都在其中诞生、运行、消亡。

理解执行上下文的创建与执行过程,能让你真正明白:

  • 为什么变量会“提升”?
  • 为什么 let 有“暂时性死区”?
  • this 到底是谁?
  • 闭包是如何“记住”外部变量的?

这些都不是魔法,而是执行上下文机制的自然结果。

📌 建议:动手画一画执行栈和上下文结构,多调试 console.log,你会对 JS 的执行模型有更深的体会。