执行上下文(Execution Context)是 JavaScript 代码执行的环境,包含了代码执行所需的所有信息。
执行上下文是 JavaScript 引擎内部的概念,用于跟踪代码的执行状态。每当函数被调用时,就会创建一个新的执行上下文。
ExecutionContext = {
// 1. 变量环境组件
VariableEnvironment: {
EnvironmentRecord: {}, // 变量存储
Outer: null, // 外部引用
},
// 2. 词法环境组件
LexicalEnvironment: {
EnvironmentRecord: {}, // 变量/函数声明
Outer: null, // 外部引用
},
// 3. this绑定
ThisBinding: undefined,
};
this 指向全局对象(浏览器中为 window)// 全局上下文
console.log(this === window); // 浏览器中为 true
var globalVar = "I'm global";
function foo() {
// 函数上下文创建
var local = "I'm local";
console.log(local);
}
foo(); // 调用时创建上下文
Eval 执行上下文eval() 函数内部代码发生在函数调用时,但代码执行前。
function example(a, b) {
var c = 10;
function inner() {}
let d = 20;
// 创建阶段发生在此行之前
console.log("执行代码");
}
// 调用前创建上下文
example(1, 2);
创建阶段完成的工作:
this 指向存储上下文中定义的:
在函数上下文中,VO 被称为 AO,包含:
function test(a, b) {
var c = 10;
function inner() {}
var d = function() {};
let e = 20;
}
// 创建阶段的 AO(伪代码):
AO = {
arguments: {
0: a,
1: b,
length: 2
},
a: undefined, // 参数
b: undefined, // 参数
c: undefined, // var 变量
d: undefined, // var 变量
inner: <function reference> // 函数声明(已赋值)
// e 不存在(let/const 在词法环境中)
}
ES6 引入的概念,替代了部分 VO/AO 的功能。
LexicalEnvironment = {
EnvironmentRecord: {
// 存储标识符
Type: "Declarative" | "Object"
},
Outer: <outer environment reference> // 指向外部环境
}
// 1. 声明式环境记录(函数、代码块)
{
x: 10,
y: 20
}
// 2. 对象环境记录(with语句、全局)
// 绑定到对象属性
后进先出(LIFO)的栈结构,用于管理执行上下文。
// 示例代码
function first() {
console.log("first");
second();
}
function second() {
console.log("second");
third();
}
function third() {
console.log("third");
}
first();
调用栈变化过程
步骤 0: 栈空
步骤 1: first() 调用
┌─────────────────┐
│ 全局上下文 │
└─────────────────┘
步骤 2: second() 调用
┌─────────────────┐
│ second() 上下文 │
├─────────────────┤
│ first() 上下文 │
├─────────────────┤
│ 全局上下文 │
└─────────────────┘
步骤 3: third() 调用
┌─────────────────┐
│ third() 上下文 │
├─────────────────┤
│ second() 上下文 │
├─────────────────┤
│ first() 上下文 │
├─────────────────┤
│ 全局上下文 │
└─────────────────┘
步骤 4: 执行完毕,依次出栈
// 递归无终止条件导致栈溢出
function infiniteRecursion() {
infiniteRecursion(); // 不断创建新上下文
}
// infiniteRecursion(); // RangeError: Maximum call stack size exceeded
console.log(a); // undefined(不是 ReferenceError!)
console.log(foo); // function
console.log(b); // ReferenceError
var a = 1;
let b = 2;
function foo() {
console.log("foo");
}
var foo = function () {
console.log("anonymous");
};
创建阶段顺序:
具体步骤:
// 1. 扫描函数声明
function foo() {
console.log("foo");
}
// 2. 扫描变量声明(var)
var a = undefined;
var foo = undefined; // 函数声明已存在,但 var 会覆盖?
// 实际优先级:函数声明 > 参数 > 变量声明
// 示例1:函数声明优先
console.log(typeof foo); // "function"
function foo() {}
var foo = "bar";
// 示例2:同名处理
function test() {
console.log(foo); // function
function foo() {}
var foo = "hello";
console.log(foo); // "hello"
}
作用域链是变量对象的链式结构,用于标识符解析。
var globalVar = "global";
function outer() {
var outerVar = "outer";
function inner() {
var innerVar = "inner";
console.log(innerVar); // "inner"(当前作用域)
console.log(outerVar); // "outer"(父作用域)
console.log(globalVar); // "global"(全局作用域)
}
inner();
}
outer();
function outer() {
// outer.[[Scope]] = [globalContext.VO]
var a = 10;
function inner() {
// inner.[[Scope]] = [outerContext.AO, globalContext.VO]
var b = 20;
console.log(a + b);
}
return inner;
}
this 绑定规则执行上下文中的 ThisBinding 根据调用方式确定:
function showThis() {
console.log(this); // 严格模式: undefined, 非严格: window
}
showThis();
const obj = {
name: "John",
sayName() {
console.log(this.name); // "John"
},
};
obj.sayName();
function greet() {
console.log(`Hello, ${this.name}`);
}
const person = { name: "Alice" };
greet.call(person); // "Hello, Alice"
function Person(name) {
this.name = name; // this 指向新创建的对象
}
const p = new Person("Bob");
const obj = {
name: "John",
sayName: () => {
console.log(this.name); // 继承定义时的 this
},
};
function createCounter() {
let count = 0; // 被 inner 函数引用
// inner 函数创建时,其 [[Scope]] 包含父级的 AO
return function inner() {
count++; // 访问父级 AO 中的 count
return count;
};
// outer 执行完毕,但其 AO 被 inner 引用,不会被销毁
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
内存结构
调用栈 堆内存
┌─────────────────────┐
全局上下文 │ 闭包(Closure) │
┌──────────────┐ │ 环境:count = 1 │
│ counter │→│ 函数:inner() │
└──────────────┘ └─────────────────────┘
{
let x = 10;
var y = 20;
}
console.log(x); // ReferenceError
console.log(y); // 20
// 每个 {} 块创建新的词法环境
for (let i = 0; i < 3; i++) {
// 每次迭代都有独立的词法环境
setTimeout(() => console.log(i)); // 0, 1, 2
}
// module.js
let privateVar = "secret"; // 模块级作用域
export function publicFunc() {
return privateVar;
}
function debugExample() {
var a = 1;
let b = 2;
debugger; // 在此处暂停
const c = a + b;
return c;
}
debugExample();
function trace() {
console.trace(); // 打印调用栈
}
function a() {
trace();
}
function b() {
a();
}
function c() {
b();
}
c();
// 不好的做法:循环中创建函数
for (var i = 0; i < 1000; i++) {
setTimeout(function () {
console.log(i);
});
}
// 好的做法:复用函数
function logNumber(num) {
console.log(num);
}
for (var i = 0; i < 1000; i++) {
setTimeout(logNumber.bind(null, i));
}
// 不需要的闭包
function createFunctions() {
var result = [];
for (var i = 0; i < 100; i++) {
result.push(function () {
return i; // 所有函数共享同一个 i
});
}
return result;
}
// 限制变量生命周期
{
let temp = computeExpensiveValue();
useValue(temp);
}
// temp 在此处不可访问,可被垃圾回收
console.log(a); // ?
var a = 1;
let b = 2;
var x = 10;
function foo() {
console.log(x); // ?
var x = 20;
}
foo();
const obj = {
name: "John",
greet: function () {
console.log(this.name);
},
arrowGreet: () => {
console.log(this.name);
},
};
obj.greet(); // ?
obj.arrowGreet(); // ?
理解执行上下文是掌握 JavaScript 核心机制的关键,涉及: