闭包使外部函数变量在执行上下文出栈后仍被保留,因内部函数持有其引用,垃圾回收标记阶段将其视为“可达”而不清除;不当使用会滞留大对象,降低回收效率。

函数执行上下文在闭包场景下会直接影响垃圾回收的标记阶段——它让本该被清除的变量持续保留在内存中,因为这些变量仍被内部函数“可达”。
执行上下文出栈 ≠ 变量立即释放
正常情况下,函数执行完毕后,其执行上下文会从执行栈中弹出,其中声明的局部变量(如 var/let/const 定义的)就失去引用,进入垃圾回收的待清理队列。但在闭包中,情况不同:
- 内部函数(如返回的 bar 或 f2)仍持有对外部函数变量(如 n、a、b)的引用
- 垃圾回收的标记阶段从全局对象(window 或 global)出发,顺着所有活跃引用链向下遍历
- 只要内部函数本身还被引用(比如赋值给了 result),它所依赖的外层变量就会被标记为“可达”,不会被清除
闭包延长了变量的生命周期
这不是 bug,而是 JavaScript 作用域链和可达性判定机制的自然结果。例如:
function f1() {
let n = 99;
return function() { console.log(n); };
}
const result = f1(); // f1 执行结束,上下文本该销毁
result(); // 仍能访问 n → n 必须保留在内存中
此时,f1 的执行上下文虽已出栈,但其中的 n 通过闭包被 result 持有,因此在下一轮标记阶段中,n 会被标记为“可到达”,跳过清除。
不当使用会干扰垃圾回收效率
如果闭包长期持有大对象(如缓存数据、DOM 节点、大型数组),而这些对象实际已不再需要,就会造成内存滞留:
- 计时器回调中引用外部大对象,且未 clearTimeout
- 事件监听器中形成闭包,监听器未移除
- 将整个外部作用域对象(如 this 或参数对象)无意暴露给内部函数
这类情况不会导致立即崩溃,但会使标记-清除周期变长、堆内存持续增长,最终拖慢运行速度。










