一、执行上下文(Execution Context)
定义:
执行上下文是 JavaScript 代码运行的动态环境,包含代码执行所需的所有信息。每当代码运行时,会创建对应的执行上下文,主要分为三类:
-
全局执行上下文
- 代码首次运行时默认创建。
- 生命周期与程序一致。
- 包含全局对象(如浏览器中的
window)和this指向全局对象。
-
函数执行上下文
- 每次函数调用时创建,函数执行完毕后销毁。
- 每个函数调用都会生成独立的上下文。
-
Eval 执行上下文
- 仅在使用
eval()时创建(不推荐使用)。
- 仅在使用
执行上下文的组成
每个执行上下文包含三个核心组件:
-
词法环境(Lexical Environment)
- 记录变量、函数声明和外部引用(作用域链)。
LexicalEnvironment = { EnvironmentRecord: { /* let/const/函数声明 */ }, outer: <父级词法环境引用> } -
变量环境(Variable Environment)
- 专门存储
var声明的变量(处理变量提升)。
VariableEnvironment = { EnvironmentRecord: { /* var声明 */ }, outer: <父级引用> } - 专门存储
-
this绑定- 指向当前执行环境的上下文对象。
二、词法环境(Lexical Environment)
定义:
词法环境是执行上下文的核心组件,用于存储变量和函数声明,并基于代码的词法结构(静态作用域)确定作用域层级。
结构:
-
环境记录(Environment Record)
- 声明式环境记录:存储变量、函数、参数(如函数内部)。
- 对象式环境记录:用于全局或
with语句(以对象形式存储)。
-
外部引用(Outer Reference)
- 指向父级词法环境,形成作用域链。
特点:
- 块级环境:
{}包裹的代码块(如if、for)会创建新的词法环境。
三、变量环境(Variable Environment)
定义:
变量环境是词法环境的一部分,专门处理 var 声明的变量,实现变量提升等特性。
与词法环境的区别:
| 特性 | 词法环境 | 变量环境 |
|---|---|---|
| 存储内容 | let、const、函数声明 | var 声明 |
| 作用域规则 | 块级作用域 | 函数/全局作用域 |
| 变量提升 | 无(存在暂时性死区) | 变量提升(初始化为 undefined) |
四、作用域(Scope)
定义:
作用域是变量和函数的可访问范围,由代码的静态结构决定。JavaScript 支持三种作用域:
-
全局作用域
- 在全局执行上下文中定义,任何位置均可访问。
-
函数作用域
- 函数内部定义的变量,仅函数内可访问。
-
块级作用域(ES6+)
- 由
{}包裹的代码块限定,仅let和const声明的变量生效。
- 由
作用域链(Scope Chain)
- 机制:当访问变量时,引擎从当前词法环境开始查找,沿外部引用逐层向外搜索。
- 静态性:作用域链在代码定义时确定,不受运行时调用位置影响。
五、核心机制解析
1. 执行上下文的生命周期
-
创建阶段
-
初始化词法环境和变量环境。
-
处理声明(变量提升):
var变量初始化为undefined。let/const变量不初始化(进入暂时性死区)。- 函数声明完全提升。
-
确定
this指向。
-
-
执行阶段
- 逐行执行代码,完成变量赋值和函数调用。
2. 块级作用域与词法环境
- 块级代码(如
if、for)不会创建新的执行上下文,但会生成块级词法环境。 - 动态性:块级词法环境在执行到代码块时动态创建,结束后销毁。
块级作用域内的变量创建也分为两种情况,通过var创建的变量在函数或者全局执行上下文创建阶段被创建,而let const定义的变量则会在代码执行到该块级代码时动态创建,这些动态创建的变量被存储在一个“块级的词法环境”中,多个块级作用域在词法环境中维护在一个类似栈的结构中,块代码执行完毕后,块作用域(词法环境)随即被移除
示例:for 循环中的块级变量
for (let i = 0; i < 3; i++) {
console.log(i); // 输出 0, 1, 2(每次迭代生成独立的块级环境)
}
console.log(i); // 报错:i 未定义
3. 变量查找机制
-
步骤:
- 当前词法环境 → 2. 外部引用指向的父级环境 → 3. 全局环境。
-
var的特殊性:
var变量存储在变量环境中,但通过作用域链仍可被查找到。
示例:作用域链查找
function outer() {
var a = "outer";
function inner() {
console.log(a); // 查找路径:inner词法环境 → outer词法环境(变量环境)
}
inner();
}
outer(); // 输出 "outer"
六、关键区别与联系
| 维度 | 执行上下文 | 作用域 |
|---|---|---|
| 定义 | 动态的运行时环境(函数调用时创建) | 静态的变量访问规则(代码定义时确定) |
| 组成 | 包含词法环境、变量环境、this | 由词法环境的作用域链实现 |
| 生命周期 | 随代码执行动态创建和销毁 | 在代码定义时固定不变 |
| 核心目标 | 管理代码执行所需的环境信息 | 确定变量和函数的可访问性 |
七、综合示例解析
let globalVar = "Global";
function outer() {
var outerVar = "Outer";
if (true) {
let blockVar = "Block";
console.log(globalVar); // 查找路径:块级环境 → outer环境 → 全局环境
}
function inner() {
console.log(outerVar); // 查找路径:inner环境 → outer环境(变量环境)
}
inner();
}
outer();
执行流程:
-
全局上下文创建:
- 词法环境记录
globalVar和outer函数。
- 词法环境记录
-
调用
outer():- 创建
outer的执行上下文,变量环境记录outerVar,词法环境记录inner函数。
- 创建
-
执行
if块:- 创建块级词法环境,记录
blockVar。
- 创建块级词法环境,记录
-
调用
inner():- 创建
inner的执行上下文,通过作用域链查找到outerVar。
- 创建
八、总结
- 执行上下文是动态的运行时环境,管理代码执行细节。
- 词法环境和变量环境共同存储变量,通过作用域链实现查找。
- 作用域是静态规则,由代码结构决定变量可访问性。
- 块级作用域通过动态创建词法环境实现,限制
let/const变量的访问范围。