javascript内存泄漏本质是本该回收的对象因强引用未断而滞留内存;可通过chrome devtools memory面板拍堆快照,对比closure、detached htmldivelement等对象增长及retaining path定位泄漏源,常见原因包括未解绑事件监听器、未清除定时器、意外全局变量、闭包持有大对象及dom强引用。

JS 中内存泄漏全景解析,核心是搞清“谁没走,为什么走不了”。不是内存用得多就叫泄漏,而是本该被回收的对象,因为某条强引用链没断,一直卡在内存里。
怎么看 Closure 泄漏最直接
打开 Chrome DevTools → Memory 面板 → 先点垃圾箱图标强制 GC → 再拍堆快照。
切换到 Constructor 视图,筛选 Closure:
- 看数量是否异常多(比如一次操作后新增几十个同名闭包)
- 点开某个 Closure,右侧看 Scope:里面有没有
hugeData、responseJson、document.getElementById(...)这类明显不该长期持有的东西 - 再看 Retainers:如果被
window、setInterval、document或某个长期存活的事件监听器引用,基本就是它了
Detached DOM 节点怎么一眼识别
在 Comparison 视图里搜 Detached HTMLDivElement 或 Detached div:
- 这类节点已从页面移除,但 JS 还拿着它(比如存进变量、缓存 Map、闭包里捕获了)
- 展开它的 Retaining Tree,顺着引用链往上找:最终大概率停在一个事件回调、定时器或全局对象上
- 常见源头:
el.addEventListener('click', handler)没配对removeEventListener,组件卸载后回调还在
定时器和全局变量怎么快速扫雷
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
- 搜
setTimeout/setInterval实例:看它们的回调函数是否捕获了this、props、大数组或 DOM 节点 - 搜
Window或globalThis下的属性:比如window.cache、window.tempList,检查是不是操作多次后持续变大 - 用 ESLint 开启
no-global-assign和no-implicit-globals,能提前拦住data = new Array(1e6)这种隐式全局
WeakMap 和 WeakRef 不是银弹
- WeakMap 的 key 必须是对象,且不阻止回收——但如果你在别处还用变量强引用了同一个 DOM 节点,它照样泄漏
- WeakRef 需手动
.deref(),返回可能为undefined;FinalizationRegistry 的回调不保证执行时机,不能靠它关 WebSocket 或释放 canvas
真正卡住内存的,往往不是技术多难,而是“以为它自己会消失”那一瞬间没断引用。










