闭包是能访问并“记住”其定义时词法作用域变量的函数,本质是作用域链的延续;易致内存泄漏因长期持有大对象或DOM引用而阻碍垃圾回收,需通过解绑事件、清理定时器、置空引用等方式避免。

闭包是 JavaScript 中一个函数,它不仅包含自身函数体,还“记住”并能访问其定义时所处的词法作用域中的变量。换句话说,只要一个函数引用了外层函数的局部变量,哪怕外层函数已经执行完毕,这个内层函数仍是闭包。
闭包的本质:作用域链的延续
JS 执行时会为每个函数创建执行上下文,其中包含词法环境(Lexical Environment)。闭包之所以能访问外部变量,是因为它的内部 [[Environment]] 引用链一直指向外层作用域。即使外层函数返回、执行栈清空,只要闭包还存在且被引用,那块词法环境就无法被垃圾回收器标记为“不可达”。
为什么闭包容易引发内存泄漏
内存泄漏不是闭包本身的问题,而是**闭包意外长期持有本该释放的大对象或 DOM 引用**,导致 GC 无法回收。关键在于:闭包延长了外部变量的生命周期,而开发者没及时切断引用链。
- 全局变量挂载闭包:比如把返回的闭包赋给全局变量,或绑定到 window 上,闭包及其捕获的整个作用域都会常驻内存
- 事件监听器未解绑:闭包作为事件回调被 addEventListener 绑定后,若忘记 removeEventListener,闭包和它引用的数据就一直存活
- 定时器持续运行:setInterval 回调是闭包,若页面卸载前没 clear,它连带引用的变量将持续占用内存
- DOM 元素移除但 JS 仍保留引用:闭包中保存了已从 DOM 树删除的节点,节点无法被回收,形成“僵尸引用”
哪些情况其实不会泄漏?
不是所有闭包都危险。如果闭包只捕获几个小字符串或数字,或者闭包本身很快被置为 null,GC 会在下一轮标记清除中顺利回收。真正危险的是:闭包持有了大数组、大对象、完整 DOM 树,又长期被全局变量、事件、定时器等强引用维系着。
立即学习“Java免费学习笔记(深入)”;
怎么避免闭包引起的泄漏
核心思路是:让闭包引用的对象在不需要时变成“不可达”。常用手段包括:
- 组件卸载或逻辑结束时,主动将闭包变量设为 null
- 用 removeEventListener 解绑事件,用 clearInterval/clearTimeout 清理定时器
- 优先使用 事件委托 替代为多个元素单独绑定闭包回调
- 对大对象引用,考虑用 WeakMap 存储,避免强引用阻碍 GC
- 避免在循环中反复创建闭包(如 for 循环里写 function() {...}),改用参数传递或提取到外层











