嵌套函数的作用域链在定义时静态确定,由[[scope]]属性记录外层词法环境引用,执行时按ao→outer→全局顺序向上查找变量;闭包是该机制的自然结果。

嵌套函数在 JavaScript 中形成多层作用域链,核心在于函数定义时就确定了它能访问哪些变量,而不是调用时。这个“能访问的范围”通过词法环境(Lexical Environment)层层链接,构成一条静态的、向上的查找路径。
函数嵌套直接生成作用域链结构
每定义一个函数,JS 引擎就为其记录一个 [[Scope]] 内部属性,里面保存着它定义时所处的所有外层词法环境的引用。当函数被调用,执行上下文创建,它的作用域链就是由这个 [[Scope]] 复制而来——从最内层的变量对象开始,逐级指向外层,直到全局环境。
- 外层函数定义 → 创建自己的词法环境,并成为内层函数
[[Scope]]的一部分 - 内层函数定义 → 把外层的词法环境记为
outer引用,形成第一级链接 - 再嵌套一层 → 新函数的
outer指向上一层函数的词法环境,继续向上串联
变量查找严格按链路向上进行
执行中遇到一个变量名,引擎不会看代码写在哪、调用位置在哪,而是从当前函数的变量对象(AO)开始查:
- 先查自己 AO 里有没有声明(
var、let、const、参数、函数声明) - 没有就顺着作用域链,去
outer指向的上一级词法环境查 - 重复该过程,直到查到全局对象(如
window或globalThis) - 全程找不到就报
ReferenceError
多层嵌套的真实链条示例
比如这段代码:
const global = 'g';<br>function a() {<br> const va = 'a';<br> function b() {<br> const vb = 'b';<br> function c() {<br> console.log(global, va, vb); // 全部可访问<br> }<br> c();<br> }<br> b();<br>}<br>a();
函数 c 的作用域链是:
c 的 AO → b 的词法环境 → a 的词法环境 → 全局词法环境
共四层。它能读 vb(自身)、va(外层)、global(顶层),但不能反向访问,也不能跳过某层直接取值。
闭包是作用域链的自然结果
只要内部函数在定义后仍被持有(比如作为返回值、赋给变量、传入异步回调),它所依赖的外层变量就不会被回收——因为作用域链还活着。这并非特殊机制,只是 JS 引擎必须保留那些被链路引用的词法环境。











