JavaScript 执行上下文与调用栈
执行上下文是 JavaScript 代码执行的环境,调用栈管理着执行上下文的执行顺序。
执行上下文类型
| 类型 | 创建时机 | 特点 |
|---|---|---|
| 全局执行上下文 | 脚本加载时 | 唯一,this 指向 window/global |
| 函数执行上下文 | 函数调用时 | 每次调用创建新的 |
| Eval 执行上下文 | eval 执行时 | 不推荐使用 |
执行上下文结构
JavaScript
┌────────────────────────────────────┐
│ 执行上下文 │
├────────────────────────────────────┤
│ Variable Environment (变量环境) │
│ - var 声明的变量 │
│ - 函数声明 │
├────────────────────────────────────┤
│ Lexical Environment (词法环境) │
│ - let/const 声明的变量 │
│ - 块级作用域 │
├────────────────────────────────────┤
│ This Binding (this 绑定) │
├────────────────────────────────────┤
│ Outer Reference (外部引用) │
│ - 作用域链 │
└────────────────────────────────────┘
执行上下文生命周期
1. 创建阶段
JavaScript
// 代码执行前
var a = 1;
let b = 2;
function foo() {}
// 创建阶段执行上下文
ExecutionContext = {
VariableEnvironment: {
a: undefined, // var 变量提升
foo: <function> // 函数声明提升
},
LexicalEnvironment: {
b: <uninitialized> // let/const 暂时性死区
},
ThisBinding: window
}
2. 执行阶段
JavaScript
// 变量赋值
ExecutionContext = {
VariableEnvironment: {
a: 1,
foo: <function>
},
LexicalEnvironment: {
b: 2
},
ThisBinding: window
}
变量提升
var 提升
JavaScript
console.log(a); // undefined(不是 ReferenceError)
var a = 1;
// 等价于
var a;
console.log(a);
a = 1;
函数声明提升
JavaScript
foo(); // "Hello" - 函数声明完整提升
function foo() {
console.log('Hello');
}
bar(); // TypeError - 函数表达式只提升变量
var bar = function() {
console.log('World');
};
let/const 暂时性死区
JavaScript
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 1;
// TDZ: 变量创建到初始化之间的区域
{
// TDZ 开始
console.log(b); // ReferenceError
let b = 2; // TDZ 结束
}
作用域链
链式查找
JavaScript
const globalVar = 'global';
function outer() {
const outerVar = 'outer';
function inner() {
const innerVar = 'inner';
console.log(innerVar); // 当前作用域
console.log(outerVar); // 外部作用域
console.log(globalVar); // 全局作用域
}
inner();
}
outer();
作用域链结构
JavaScript
inner 执行上下文
↓ [Outer Reference]
outer 执行上下文
↓ [Outer Reference]
全局执行上下文
调用栈(Call Stack)
工作原理
JavaScript
function first() {
console.log('first start');
second();
console.log('first end');
}
function second() {
console.log('second');
}
first();
// 调用栈变化
// 1. [global]
// 2. [global, first]
// 3. [global, first, second]
// 4. [global, first] ← second 出栈
// 5. [global] ← first 出栈
栈溢出
JavaScript
function recurse() {
recurse(); // 无限递归
}
recurse();
// RangeError: Maximum call stack size exceeded
调用栈最大深度
JavaScript
// 不同引擎限制不同
// V8 约 10000 层
function testStack(depth = 0) {
try {
return testStack(depth + 1);
} catch (e) {
return depth;
}
}
console.log(testStack()); // 约 10000+
执行上下文示例分析
JavaScript
const x = 10;
function foo(a) {
var b = 20;
let c = 30;
function bar() {
let d = 40;
console.log(a, b, c, d, x);
}
bar();
}
foo(5);
// 执行上下文创建顺序
// 1. 全局执行上下文
// 2. foo 函数执行上下文
// 3. bar 函数执行上下文
全局执行上下文
JavaScript
GlobalExecutionContext = {
LexicalEnvironment: {
x: 10
},
ThisBinding: window
}
foo 执行上下文
JavaScript
FooExecutionContext = {
VariableEnvironment: {
b: undefined → 20,
bar: <function>
},
LexicalEnvironment: {
a: 5,
c: <uninitialized> → 30
},
OuterReference: GlobalExecutionContext,
ThisBinding: window
}
bar 执行上下文
text
BarExecutionContext = {
LexicalEnvironment: {
d: 40
},
OuterReference: FooExecutionContext,
ThisBinding: window
}
this 绑定确定时机
text
const obj = {
name: 'obj',
greet: function() {
console.log(this.name);
}
};
const greet = obj.greet;
obj.greet(); // 'obj' - obj 调用
greet(); // undefined - window 调用
// this 在执行上下文创建时确定
// 而不是词法分析时
执行上下文在函数调用时创建,this 绑定取决于调用方式。
要点总结
| 概念 | 说明 |
|---|---|
| 执行上下文 | 代码执行的环境 |
| 调用栈 | 管理执行上下文的栈结构 |
| 变量提升 | var 和函数声明提前 |
| TDZ | let/const 初始化前的区域 |
| 作用域链 | 变量查找链路 |
- 执行上下文包含变量环境、词法环境、this 绑定
- 调用栈后进先出(LIFO)
- var 提升值为 undefined,函数声明提升整个函数
- 作用域链由词法作用域决定
📝 发现内容有误?点击此处直接编辑