闭包导致变量不被回收的根本原因是其维持了可达性链:只要闭包函数被持续持有(如赋值给变量、绑定事件或存于定时器),其捕获的变量就始终可达,无法被垃圾回收;且闭包仅保留实际访问的变量,非全量捕获。

因为闭包内存储的引用被持续持有,而 JavaScript 的垃圾回收机制只释放“不可达”的值——只要闭包函数本身还存在(比如被赋给变量、绑定在事件上、存于定时器中),它所捕获的变量就始终可达,自然不会被销毁。
闭包让变量“活下来”的根本原因
函数执行完毕后,局部变量本该随执行上下文销毁。但一旦内部函数引用了这些变量,并且该内部函数被返回或传出作用域,引擎就会将这些变量从栈迁移至堆,并通过 [[Environment]] 持续关联。这个关联就是“可达性链”的一部分:全局变量 → 闭包函数 → 外部词法环境 → 变量。只要链条没断,变量就不会被回收。
防抖场景中闭包引用为何长期有效
防抖函数通常返回一个包装后的函数,该函数内部保存着上一次的定时器 ID 和参数等状态。这些状态变量之所以不消失,是因为:
- 返回的防抖函数被事件监听器(如
input或scroll)持续调用,自身一直被引用 - 它内部通过闭包访问并修改
timerId、args、thisArg等,形成强引用关系 - 即使原防抖工厂函数(如
debounce(fn, ms))早已执行完毕,其作用域因被闭包持有而保留在堆中
哪些情况会让这些变量真正被释放?
闭包变量不是“永远不销毁”,而是取决于闭包是否还被使用。以下操作可切断引用链,触发回收:
- 显式将防抖函数赋值的变量设为
null或重新赋值(如handleInput = null) - 移除 DOM 事件监听器(
element.removeEventListener('input', handleInput)) - 组件卸载时清空定时器并解除所有闭包引用(React 中在
useEffect清理函数里清除timerId并置空回调)
关键提醒:闭包只保留实际用到的变量
不是外部函数里所有变量都会被闭包捕获。例如:
防抖函数内部若只用到了timerId 和 fn,而没访问 bigDataArray,那 bigDataArray 在工厂函数执行完后就会被立即回收——闭包按需捕获,不搞“全盘继承”。

















