闭包能驻留的根本原因是函数对象的[[Scope]]属性在定义时就固化了对外层词法环境的强引用,只要该函数仍被引用,其作用域链中的外层环境就不会被垃圾回收。

闭包能“驻留”在函数执行栈销毁之后,根本原因不是变量被刻意保留,而是作用域链的结构本身决定了外层词法环境不会随执行上下文弹出而自动释放——只要内层函数仍被引用,它所依赖的作用域链节点就持续有效。
作用域链不是运行时动态拼接的路径,而是创建时就固化下来的引用链
函数在定义(而非调用)时,V8 就会把其外层词法环境的引用存入内部属性 [[Scope]]。这个属性是一个只读的、静态的环境对象数组,例如:
- 全局函数的 [[Scope]] = [globalEnv]
- 嵌套在函数 A 中的函数 B 的 [[Scope]] = [A 的词法环境, globalEnv]
这个链在函数对象诞生那一刻就确定了,和后续是否调用、是否执行完毕无关。执行上下文只是临时运行容器;而 [[Scope]] 是函数对象自身的固有属性,生命周期绑定函数本身。
闭包驻留的本质:函数对象持有了对外层词法环境的强引用
当函数 B 被返回或赋值给外部变量(如 const fn = A()),B 这个函数对象依然存在。由于 B 的 [[Scope]] 中明确包含 A 的词法环境(即 A 执行时创建的 LexicalEnvironment),而 JS 引擎的垃圾回收器遵循“可达性”原则——只要 A 的词法环境能从全局变量、闭包函数等根对象被访问到,它就不会被回收。
换句话说:不是 A 的执行上下文没销毁,而是它的词法环境被 B “抓着不放”;即使 A 已经执行完、上下文早已弹出调用栈,它的变量对象(AO/VO)仍存活在堆内存中。
AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。
为什么普通局部变量不驻留?关键在“谁在引用它”
函数 A 内部用 let x = 100 声明的变量,确实也存在 A 的词法环境中。但它能否驻留,取决于是否有闭包函数将其纳入自己的作用域链:
- 如果没有内层函数访问
x,A 执行结束 → 词法环境无外部引用 → 被 GC 回收 - 如果有内层函数 B 访问
x,B 的 [[Scope]] 包含 A 的词法环境 →x随该环境一起被保留
所以驻留与否,不取决于变量声明方式(let/var/const),而取决于它是否出现在某个现存函数的作用域链中。
一个直观验证:看 Chrome DevTools 的 Closure 面板
在调试时打个断点,打开 Scope 面板,你会看到:
- Closure 条目下明确列出被捕获的变量名及当前值
- 这些变量所属的词法环境,其内存地址与 A 执行时创建的环境一致(可比对)
- 即使 A 函数早已退出,该 Closure 环境仍显示为 “active”
这说明:JS 引擎没有销毁它,是因为它还在作用域链中“上岗”,不是“退休”。

















