闭包延长的是堆中词法环境的寿命,而非栈中变量;它通过静态绑定[Environment]属性使堆内存中的环境记录被持续引用,导致垃圾回收受阻,需主动管理引用链以防内存泄漏。

闭包不影响执行栈的结构和生命周期,它真正影响的是堆内存中词法环境的存活时间——栈里只管函数“进”和“出”,而闭包让堆里的变量“赖着不走”。
执行栈只调度,不保留变量
每次函数调用,JS 引擎新建一个执行上下文压入栈顶;函数返回,上下文立刻弹出销毁。栈中不留变量、不存作用域,只保存对堆中 LexicalEnvironment 的引用指针。
- 栈内存空间小、连续、自动清理,适合存基本类型和执行状态
- 外层函数执行完毕,其执行上下文已从栈中消失,但闭包仍能访问它的变量
- 所谓“变量还在”,不是栈没清掉,而是堆里那块环境记录还被闭包牢牢引用着
闭包延长的是堆中词法环境的寿命
闭包形成时,内部函数的 [[Environment]] 属性会静态绑定定义时的词法环境,而这个环境对象本身分配在堆内存中,生命周期独立于栈帧。
- let/const 声明的变量,在闭包中不是拷贝值,而是直接读写堆中那个绑定位置
- 多个闭包(如 inc 和 get)可共享同一份堆环境记录,修改互相可见
- 只要至少一个闭包函数还可达(比如赋给全局变量、挂在事件监听器上),相关堆环境就不会被垃圾回收
不当使用容易引发内存泄漏
闭包本身无害,但它会阻止垃圾回收器释放本该回收的数据。问题常出现在长期持有大对象或 DOM 节点的场景。
立即学习“Java免费学习笔记(深入)”;
- 事件监听器未解绑:回调函数是闭包,持续引用节点和数据
- 定时器未清除:setInterval 回调持有了 bigData,导致整个对象驻留堆中
- 循环中用 var 绑定事件:所有回调共享同一个 i 变量,i 无法释放,且值错乱
- 意外捕获整个 DOM 元素:应只存 id 或必要字段,而非 node 对象本身
可控使用的关键是管理引用链
闭包延长变量生命周期是特性,不是 bug。关键在于清楚谁在引用、何时该断开。
- 不再需要闭包时,显式设为 null 或重新赋值,帮助 GC 判定不可达
- 用 let 替代 var 避免循环变量污染,块级作用域天然隔离
- 大量私有数据优先用 WeakMap 存储,键名对象被回收时不会阻止
- 事件监听器用完后调用 removeEventListener,定时器记得 clearInterval


















