JavaScript 的词法作用域(Lexical Scope,又称静态作用域)是理解函数行为、闭包和变量访问规则的核心概念。以下是深入解析:
一、词法作用域的定义
词法作用域(Lexical Scope):
函数的作用域在函数定义时就已经确定,而非运行时确定。作用域链基于代码的物理结构(即书写时的嵌套关系),与函数调用位置无关。
示例 1:作用域链的静态性
let globalVar = "global";
function outer() {
let outerVar = "outer";
function inner() {
let innerVar = "inner";
console.log(globalVar); // 可以访问所有外层变量
}
inner();
}
outer(); // 输出 "global"
即使 inner 函数在 outer 外部被调用,依然只能访问定义时的作用域链。
二、词法作用域的核心规则
1. 作用域层级
• 全局作用域:最外层环境
• 函数作用域:每个函数创建自己的作用域(ES5)
• 块级作用域:{} 配合 let/const(ES6)
2. 变量查找机制
访问变量时,引擎会按以下顺序查找:
当前函数作用域 → 外层函数作用域 → ... → 全局作用域
示例 2:跨作用域访问
function parent() {
let money = 100;
return function child() {
console.log(money); // 访问父级作用域的变量
money--;
};
}
const childFn = parent();
childFn(); // 100
childFn(); // 99 (闭包保留作用域)
三、与动态作用域(Dynamic Scope)的对比
| 特性 | 词法作用域 | 动态作用域 |
|---|---|---|
| 作用域确定时机 | 函数定义时 | 函数调用时 |
| 变量查找依据 | 代码书写结构 | 调用栈顺序 |
| JavaScript 是否采用 | ✅ | ❌(但 this 类似动态) |
示例 3:词法 vs 动态
let x = 10;
function foo() {
console.log(x);
}
function bar() {
let x = 20;
foo();
}
bar(); // 输出 10(词法作用域),若动态作用域会输出 20
四、闭包与词法作用域
闭包(Closure)是词法作用域的直接产物:函数可以记住并访问其定义时的作用域链,即使函数在其他地方被执行。
示例 4:经典的闭包陷阱
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // 输出 3, 3, 3
}, 100);
}
原因:
所有回调共享同一个 i(存在于全局作用域),循环结束后 i=3
修复(利用块级作用域):
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // 输出 0, 1, 2
}, 100);
}
let 为每次迭代创建独立的作用域。
五、词法作用域的实际应用
1. 模块模式(Module Pattern)
const counterModule = (() => {
let count = 0; // 私有变量
return {
increment: () => ++count,
getCount: () => count
};
})();
counterModule.increment();
console.log(counterModule.getCount()); // 1
2. 高阶函数
function multiplyBy(n) {
return (x) => x * n; // 记住定义时的 n
}
const double = multiplyBy(2);
console.log(double(5)); // 10
六、常见误区与调试技巧
1. 变量遮蔽(Variable Shadowing)
let x = 10;
function test() {
let x = 20; // 遮蔽全局的 x
console.log(x); // 20
}
test();
2. 意外的全局变量
function leak() {
value = 42; // 未用 let/const/var → 自动成为全局变量
}
leach();
console.log(value); // 42(污染全局)
3. 调试工具
使用 Chrome DevTools 的 Scope 面板查看闭包作用域:

七、总结
• 词法作用域是 JavaScript 的核心设计,决定了变量的可见性规则
• 闭包 = 函数 + 词法环境,是模块化编程的基础
• 优先使用 let/const 避免变量提升和遮蔽问题
• 理解作用域链可有效解决变量访问相关的 Bug
通过掌握词法作用域,开发者可以更精准地控制变量的生命周期,编写出高效、可维护的闭包代码。
655

被折叠的 条评论
为什么被折叠?



