addEventListener 会引发内存泄漏,但仅当事件监听器未被移除且监听对象长期存活时;常见于组件销毁未清理、全局监听未解绑、闭包引用大型对象等场景。
addEventListener 会引发内存泄漏吗
会,但只在特定条件下——核心是「事件监听器没被移除」且「监听对象长期存活」。比如给全局
或长生命周期的 DOM 节点(如
)反复绑定匿名函数,又不调用
,就容易让监听函数和闭包中引用的变量无法被 GC 回收。
常见错误场景:
组件销毁时(如 React unmount、Vue beforeUnmount)忘了清理
用
做全局快捷键,但页面跳转后没解绑
监听器里直接引用了大型对象或整个组件实例(如
),而该实例本该被释放
用 addEventListener 的第三个参数规避隐式绑定风险
传入
或
不仅能优化性能,还能减少泄漏可能——因为
会让浏览器自动清理监听器,无需手动调用
。
适用情况:
立即学习
“
前端免费学习笔记(深入)
”;
:适合只响应一次的交互,比如初始化后的点击引导、表单提交成功后的关闭逻辑
:适用于
、
等不允许调用
的场景,避免主线程阻塞,也间接降低因频繁触发导致的闭包驻留时间
注意:
和
不能共存于同一监听器(Chrome 会警告),需按需取舍
示例:
用 WeakMap 存储监听器引用以支持安全解绑
当必须动态绑定/解绑、又无法保证监听器函数引用一致时(比如每次渲染都生成新箭头函数),直接用
会失败——因为函数引用已变。这时可以用
显式保存原始监听器,确保解绑准确。
使用HTML,CSS,JavaScript开发Android应用程序 英文文字pdf版附源文件
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
下载
操作要点:
把目标元素作为
的 key,对应监听器函数作为 value
绑定时存入,销毁前遍历
并逐个
自动随元素回收,不会阻止 GC,比普通
更安全
示例:
现代框架中别依赖 ref.current 直接绑事件
React 的
或 Vue 的
在组件卸载后,其
可能仍指向旧 DOM 节点,若此时还往上面绑事件,就会形成孤立监听器+闭包引用,典型泄漏路径。
正确做法:
React 中优先用
的清理函数,配合稳定函数引用(如
)
Vue 中在
钩子内显式解绑,不要在
里用
后就不管了
避免在事件回调中直接读取未 memoized 的 props 或 state,否则闭包会拖住整块 render 上下文
最易被忽略的一点:监听器函数本身是否捕获了不该长期持有的对象(比如一个未清理的定时器 ID、一个未释放的 Canvas context、一个仍在轮询的
)。这类泄漏和事件绑定无关,但常伴随发生。
window#appremoveEventListeneraddEventListenerdocument.addEventListener('click', () => { ... })this{ once: true }{ passive: true }once: trueremoveEventListeneronce: truepassive: truescrolltouchstartpreventDefault()oncepassivebutton.addEventListener('click', handler, { once: true });removeEventListenerWeakMapWeakMapWeakMapremoveEventListenerWeakMapMapconst listenerMap = new WeakMap();
function bindClick(el, fn) {
listenerMap.set(el, fn);
el.addEventListener('click', fn);
}
function unbindClick(el) {
const fn = listenerMap.get(el);
if (fn) el.removeEventListener('click', fn);
listenerMap.delete(el);
}useRefref.currentuseEffectuseCallbackonBeforeUnmountmountedref.value.addEventListenerAbortController