作用域链(Scope Chain) 是 JavaScript 中用于变量和函数查找的机制。它决定了代码在访问变量时如何沿着嵌套的作用域层次逐级向上查找。
核心概念
- 作用域(Scope)
变量或函数的可访问范围。JavaScript 有三种作用域:- 全局作用域(Global Scope)
- 函数作用域(Function Scope)
- 块级作用域(Block Scope,
let/const)
- 词法作用域(Lexical Scope)
作用域在代码编写时(定义时)就已经确定,而非运行时。 - 作用域链的形成
当函数被定义时,它会保存一个对其父级作用域的引用。这种嵌套关系形成一个链式结构,即作用域链。
作用域链的查找规则
- 当访问一个变量时,JavaScript 引擎会从当前作用域开始查找。
- 如果当前作用域没有找到,则沿着作用域链向外层作用域逐级查找。
- 直到找到变量或到达全局作用域(如果全局作用域也没有,则报错
ReferenceError)。
let globalVar = "全局变量";
function outer() {
let outerVar = "外层变量";
function inner() {
let innerVar = "内层变量";
console.log(innerVar); // 当前作用域找到
console.log(outerVar); // 向上查找到 outer 作用域
console.log(globalVar); // 继续向上查找到全局作用域
console.log(notExist); // 全部未找到,报错!
}
inner();
}
outer();
作用域链结构(由内到外):
inner 作用域 → outer 作用域 → 全局作用域
关键点
- 与闭包的关系
闭包的本质是函数保留了对其定义时作用域链的引用,即使在其外部执行,也能访问原始作用域链中的变量。
function createCounter() {
let count = 0;
return function() {
count++; // 访问外部函数的变量
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1(通过闭包访问 count)
- 动态修改作用域链(仅限历史遗留特性)
with语句(已弃用)会临时在作用域链前端插入一个对象。eval可能意外修改作用域(严格模式下限制)。
- 全局作用域是链的顶端
所有作用域链最终都会追溯到全局作用域(浏览器中为window)。
与原型链的区别
| 作用域链 | 原型链 |
|---|---|
| 用于查找变量/函数 | 用于查找对象的属性 |
| 基于函数/块级作用域的嵌套 | 基于对象的原型继承 |
| 在函数定义时静态形成 | 在对象创建时通过 __proto__ 链接 |
总结
- 作用域链是 JavaScript 实现变量访问的底层机制。
- 它遵循词法作用域规则,在函数定义时确定。
- 理解作用域链是掌握闭包、内存管理、代码调试的关键。
通过开发者工具的断点调试,可以直观查看作用域链中的变量(Scope面板),帮助深入理解执行过程。