作用域链是函数定义时由嵌套词法环境通过outer引用逐级连接形成的静态查找路径,决定变量访问位置、可达性及具体值;它自当前词法环境向上查找直至全局,与调用位置无关,是闭包、循环中异步问题等现象的根本原因。

JavaScript作用域链是一条由多个执行上下文的作用域按嵌套顺序连接而成的查找路径,它决定了变量和函数在代码中**从哪里被访问、能否被访问、以及访问到的是哪个值**。
作用域链是怎么形成的?
每当函数被调用,JS引擎就会为其创建一个执行上下文(Execution Context),其中包含自己的词法环境(Lexical Environment)。这个环境内部保存了该函数定义时所在的作用域信息,并通过 outer 引用指向其外层词法环境——层层向上,直到全局环境。这一串引用关系就构成了作用域链。
- 函数定义时就确定了它的 outer 指向(静态/词法绑定),和函数在哪调用无关
- 全局作用域没有 outer,是链的终点
- 每次函数调用都生成新执行上下文,但作用域链结构由定义位置决定
变量查找怎么依赖作用域链?
当访问一个变量(比如 console.log(x)),JS引擎会沿着当前执行上下文的词法环境开始查找:
- 先查当前词法环境自身的记录(如函数参数、let/const 声明)
- 没找到,就顺着 outer 引用进入上一级词法环境查找
- 持续向上,直到找到变量或抵达全局环境
- 若全程未找到,就报 ReferenceError
例如:
立即学习“Java免费学习笔记(深入)”;
function outer() {
let x = 'outer';
function inner() {
console.log(x); // 找不到自己的x → 查outer环境 → 找到'outer'
}
inner();
}
outer();
常见影响:闭包与变量“捕获”
因为作用域链在函数定义时就固定,所以内部函数即使在外部调用,也能访问定义时的外层变量——这就是闭包的核心机制。
- 返回的函数保留了对原作用域链的引用,即使外层函数已执行完毕
- 多个内部函数共享同一外层词法环境(注意循环中 var 和 let 的区别)
- 容易导致意料之外的变量复用或内存无法释放(尤其在事件监听、定时器中)
和 this 绑定无关,别混淆
作用域链只管 标识符(变量名、函数名)的解析;而 this 是运行时基于调用方式动态确定的,两者机制完全不同。不要以为“能访问变量”就代表“this 指向那里”。
基本上就这些。理解作用域链不是背概念,关键是明白“定义位置决定查找路径”,很多看似奇怪的行为——比如闭包、for 循环中 setTimeout 输出相同值、模块私有变量——背后都是它在起作用。











