作用域链是函数定义时确定的静态嵌套路径,按[[Environment]]逐层向上查找变量:先当前LexicalEnvironment,再outer指向的外层环境,直至全局;this和eval不参与该链,闭包捕获变量引用而非值快照。

作用域链本质是词法作用域的查找路径
JavaScript 的作用域链不是运行时动态构建的“链表”,而是函数定义时就确定的嵌套关系。每次执行函数,引擎会按 [[Environment]] 内部槽记录的外层词法环境逐层向上找变量——从当前执行上下文的词法环境开始,到外层函数的词法环境,再到全局环境。
关键点:查找只看函数在哪定义,不看在哪调用。闭包能“记住”外层变量,正是因为这个静态链在函数创建时就固化了。
变量查找规则:先查当前词法环境,再沿 outer 指针向上遍历
查找过程严格遵循以下顺序:
- 检查当前执行上下文的
LexicalEnvironment(含let/const声明) - 若未找到,检查其
outer引用指向的上一级词法环境(通常是外层函数的LexicalEnvironment) - 继续向上,直到到达全局环境(
globalThis对应的词法环境) - 仍找不到则抛出
ReferenceError
注意:var 声明会被提升到函数作用域顶部,但查找逻辑不变;with 和 catch 语句会临时插入环境,但现代代码应避免使用。
立即学习“Java免费学习笔记(深入)”;
常见陷阱:this 不参与作用域链查找,eval 会污染当前环境
this 是执行上下文的一个独立属性,和词法作用域无关。写 this.x 查的是对象属性,不是沿着作用域链找变量。
eval() 在非严格模式下,如果传入字符串包含变量声明,会把它们注入当前词法环境,导致意外覆盖或泄漏。例如:
function foo() {
var a = 1;
eval('var a = 2;');
console.log(a); // 2 —— 不是作用域链问题,是 eval 动态修改了当前环境
}
其他易错点:
- 箭头函数没有自己的
arguments、super、new.target,也不绑定this,但作用域链完全继承外层 - 模块顶层是块级作用域,不是全局,所以
var声明不会挂到window上 - 类体内部是严格模式,且
let/const声明不会被提升到类作用域顶部
调试技巧:用 debugger + 浏览器开发者工具看 Scope 面板
在函数内设断点,打开 Chrome DevTools 的 “Scope” 面板,能看到当前执行上下文的:
-
Closure:对应函数的[[Environment]],显示它捕获的外层变量 -
Script:模块或脚本顶层的词法环境 -
Global:全局环境
注意:如果看到某个变量没出现在 Closure 里,说明它没被该函数“引用”,V8 可能已优化掉闭包绑定——这不是 bug,是引擎正常行为。
真正容易被忽略的是:作用域链只决定变量能否被访问,不决定变量值是否“实时更新”。闭包捕获的是变量的引用,不是快照;但如果外层变量被重新赋值,闭包里读到的就是新值。











