执行上下文与作用域链
理解 JavaScript 中执行上下文的创建过程和作用域链的查找机制
问题
解释 JavaScript 中的执行上下文(全局、函数、块级)和作用域链是如何工作的。
解答
执行上下文
执行上下文是 JavaScript 代码执行时的环境,包含变量、函数声明、this 等信息。
// 1. 全局执行上下文 - 程序启动时创建
var globalVar = 'global';
function outer() {
// 2. 函数执行上下文 - 函数调用时创建
var outerVar = 'outer';
function inner() {
// 3. 另一个函数执行上下文
var innerVar = 'inner';
console.log(innerVar, outerVar, globalVar);
}
inner();
}
outer(); // 输出: inner outer global
执行上下文的组成
// 执行上下文包含三个部分
const ExecutionContext = {
// 1. 变量环境 - 存储 var 声明和函数声明
VariableEnvironment: {},
// 2. 词法环境 - 存储 let/const 声明
LexicalEnvironment: {},
// 3. this 绑定
ThisBinding: undefined
};
块级作用域
function blockScopeDemo() {
var a = 1; // 函数作用域
let b = 2; // 块级作用域
const c = 3; // 块级作用域
if (true) {
var a = 10; // 同一个 a,被覆盖
let b = 20; // 新的 b,只在 if 块内有效
const c = 30; // 新的 c,只在 if 块内有效
console.log(a, b, c); // 10 20 30
}
console.log(a, b, c); // 10 2 3
}
blockScopeDemo();
作用域链
作用域链在函数定义时确定,而非调用时。
var x = 10;
function foo() {
console.log(x); // 沿作用域链查找 x
}
function bar() {
var x = 20;
foo(); // 输出 10,不是 20
}
bar();
// foo 的作用域链: foo -> 全局
// 在 foo 定义时就确定了,与调用位置无关
作用域链查找过程
var a = 'global a';
function outer() {
var b = 'outer b';
function middle() {
var c = 'middle c';
function inner() {
var d = 'inner d';
// 查找顺序: inner -> middle -> outer -> global
console.log(d); // inner d (当前作用域找到)
console.log(c); // middle c (上一层找到)
console.log(b); // outer b (再上一层找到)
console.log(a); // global a (全局作用域找到)
}
inner();
}
middle();
}
outer();
闭包与作用域链
function createCounter() {
let count = 0; // 被闭包捕获
return {
increment() {
count++;
return count;
},
getCount() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
// increment 和 getCount 的作用域链中保留了对 count 的引用
执行上下文栈
function first() {
console.log('first start');
second();
console.log('first end');
}
function second() {
console.log('second start');
third();
console.log('second end');
}
function third() {
console.log('third');
}
first();
// 执行上下文栈变化:
// 1. [Global]
// 2. [Global, first]
// 3. [Global, first, second]
// 4. [Global, first, second, third]
// 5. [Global, first, second] <- third 执行完出栈
// 6. [Global, first] <- second 执行完出栈
// 7. [Global] <- first 执行完出栈
关键点
- 执行上下文分三种:全局、函数、eval(块级作用域不创建新的执行上下文)
- 作用域链在函数定义时确定,不是调用时(词法作用域)
var是函数作用域,let/const是块级作用域- 变量查找沿作用域链从内向外,找到即停止
- 闭包能访问外部变量,是因为作用域链的引用被保留
目录