闭包是函数与其词法环境的组合,使内部函数能持续访问外部函数执行完毕后仍存在的变量;形成条件是返回函数引用了未销毁的外部变量,且该函数值被保留。

闭包不是语法糖,而是函数与其词法环境的组合;它让内部函数能持续访问外部函数定义时的变量,哪怕外部函数早已执行完毕。
闭包是怎么形成的
当一个函数返回另一个函数,且返回的函数引用了外层函数的局部变量时,就形成了闭包。关键不在“嵌套”,而在“引用未销毁的外部变量”。
- 必须是函数值被保留(如赋值给变量、作为参数传出去、存入数组或对象)
-
var声明的变量在函数作用域内仍会被闭包捕获,但要注意变量提升带来的共享问题 -
let和const声明的变量在循环中会为每次迭代创建独立绑定,更适合构造多个闭包实例 - 常见误判:只看函数嵌套结构,忽略是否真有变量被“捕获”——没引用外部变量的内层函数不算闭包
为什么 for 循环里用 var 定义 i 会导致闭包“记错值”
因为 var 是函数作用域,整个循环共用一个 i 变量。所有闭包都指向同一个内存地址,等循环结束、异步回调执行时,i 已变成 3(假设循环 3 次)。
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出 3, 3, 3
}
- 修复方式一:用
let替代var→ 每次迭代生成独立绑定 - 修复方式二:立即执行函数包裹,把当前
i作为参数传入 →(function(j) { setTimeout(() => console.log(j), 100); })(i) - 修复方式三:用
forEach替代for→ 回调函数参数天然隔离
闭包导致内存无法释放的典型场景
闭包本身不必然造成内存泄漏,但若闭包长期存活且引用了大对象(如 DOM 节点、大型数组),而这些对象又不再需要,就会阻止垃圾回收。
立即学习“Java免费学习笔记(深入)”;
- 事件监听器中保存对 DOM 元素的引用,且监听器未被移除 → 元素和其父树都无法回收
- 定时器(
setInterval)回调中闭包持有大数据缓存,且定时器未清除 - 将闭包赋值给全局变量或长生命周期对象(如
window.cache) - 调试技巧:Chrome DevTools 的 Memory 面板录制堆快照,对比前后,筛选“Closure”类型对象
真正难处理的不是闭包本身,而是闭包引用链中那些你没意识到还活着的对象——比如一个按钮点击回调闭包,悄悄拖住了整张页面的数据模型。









