闭包变量不被回收的核心前提是:外部函数执行完毕后,其词法环境仍被内部函数作用域链引用,且该内部函数本身被活跃根对象(如全局变量、事件监听器、定时器)可达;一旦引用断开,变量将在下一轮标记清除中被回收。

JS 垃圾回收机制对闭包变量的保留,核心只看一点:是否还有活跃的引用链能到达该变量。只要闭包内部函数还被持有(比如赋值给变量、作为事件回调、存在定时器中),它所捕获的外部变量就不会被回收。
闭包变量不被回收的前提
闭包变量能“活下来”,必须同时满足两个条件:
- 外部函数已执行完毕,但其词法环境(包含局部变量)仍被内部函数的作用域链引用
- 该内部函数本身仍被某个活跃的根对象可达(例如全局变量、DOM 元素属性、定时器回调队列、事件监听器等)
哪些情况会让闭包变量持续驻留内存
以下操作会维持对闭包变量的引用,阻止垃圾回收:
- 把返回的内部函数赋值给一个全局变量或长期存活的对象属性(如
const fn = outer()) - 将闭包用作事件监听器且未调用
removeEventListener - 在
setInterval或setTimeout中使用闭包,且未调用clearInterval/clearTimeout - 闭包中意外持有了大型对象(如大数组、完整 DOM 树)、又未在后续逻辑中释放引用
什么时候闭包变量会被回收
闭包变量不是永久存在的,回收时机取决于引用是否真正断开:
- 显式将闭包函数变量设为
null(如fn = null),且无其他引用指向它 - 闭包函数是某个对象的属性,而该对象本身被销毁(如 DOM 元素被
remove(),且无其他 JS 引用) - 在模块作用域中定义的闭包,整个模块被卸载(如动态
import()后又触发清理逻辑) - 现代浏览器中,一旦闭包函数不可达,其关联的整个外层词法环境(包括所有被捕获变量)会在下一轮标记清除周期中被一并回收
注意:不是所有闭包都导致内存泄漏
闭包本身是正常语言特性,只有当本该释放的变量因闭包持续被引用才构成问题。比如:
- 计数器函数
createCounter()持有count是合理且必要的 - 但若它同时持有一个 10MB 的图片数据缓存,且后续再没用过,就属于可优化的冗余引用
-
let/const声明的变量不会挂到window上,但闭包对其的引用依然有效——回收与否与是否挂载无关,只与可达性有关











