闭包是JavaScript中函数与其捕获的词法环境捆绑的运行时现象;它实现私有状态、缓存、事件上下文保持和柯里化,但易致内存泄漏,需通过清理引用、限制缓存、事件委托等手段安全使用。
闭包 是 JavaScript 中一个函数和它所捕获的词法环境(也就是定义时所在作用域中的变量)捆绑在一起的组合。它不是语法结构,而是一种运行时现象:只要内层函数引用了外层函数的局部变量,并且这个内层函数在外部被调用,就形成了闭包。
下面这段代码里,inner 就是一个闭包:
function outer() {
const count = 0;
return function inner() {
return ++count;
};
}
const counter = outer();
console.log(counter()); // 1
console.log(counter()); // 2outer 执行完后本该销毁 count,但 inner 还“记得”它,所以 count 持续存活——这就是闭包的本质。
它不是炫技工具,而是解决具体问题的手段:
count 值memoize 函数,用闭包保存 Map 或 WeakMap 缓存i 或 item.id,避免所有回调都用最后一个值const add5 = add(5),靠闭包锁住第一个参数闭包本身不危险,危险的是它让变量“赖着不走”。常见踩坑点:
element 或 dataset,却没在元素移除时调用 removeEventListener
setInterval 回调里的闭包仍强引用组件实例(this 或 ref),导致整棵树无法回收memoize)无上限增长,且缓存项本身又含闭包,形成引用链闭环JSON.parse 后的完整响应体),后续不再需要却没手动设为 null
判断是否泄漏:打开 Chrome DevTools → Memory 面板 → 录制堆快照,筛选 Closure 类型,看是否有异常增长的闭包对象及其引用链。
不是不用,而是有意识地控制生命周期:
WeakMap 存关联数据,而非闭包持引用clearInterval(id)、abortController.abort()、removeEventListener
memoize(fn, { max: 100 }),或用 Map + setTimeout 实现 LRU 过期机制闭包的复杂性不在语法,而在它把“变量何时释放”从自动变成了你得亲手过问的事。