应清理组件卸载时的定时器、事件监听器和IntersectionObserver实例,避免内存泄漏;避免闭包意外捕获大对象;慎用链式数组方法;减少强制回流;优先使用transform/opacity动画。

避免闭包意外捕获大对象
闭包本身不是问题,但容易在无意中让本该被回收的对象长期驻留内存。典型场景是事件监听器里引用了 DOM 节点或大型数据结构。
- 检查
addEventListener回调是否直接访问了外层作用域的largeDataArray或document.getElementById('app')等;能传参就别靠闭包捕获 - 用
WeakMap缓存实例私有数据,比用闭包 + 普通对象更安全,避免强引用阻止 GC - 监听器不用时务必调用
removeEventListener,尤其在单页应用组件卸载阶段——漏掉这点,DOM 节点和关联闭包全卡在内存里
数组操作别无脑用 map/filter 链式调用
连续调用 map、filter、reduce 会创建多个中间数组,对万级以上数据就是明显性能拖累。
- 优先用一次
for循环完成多步逻辑,尤其是需要条件筛选 + 变换 + 聚合的场景 - 真要用函数式风格,考虑
Array.prototype.flatMap替代map().flat(),减少临时数组;或用lodash.chain()(注意它内部也做优化,但仍有开销) -
Array.from(new Set(arr))去重比[...new Set(arr)]稍快,但两者都比手写for+Object.hasOwn查表慢 3–5 倍(尤其 arr > 10k)
定时器与异步任务不清理等于内存泄漏
setTimeout、setInterval、requestIdleCallback 返回的 ID 是“活引用”,只要没清除,回调闭包及其捕获的变量就无法被回收。
- 组件销毁、模块卸载时,必须显式调用
clearTimeout(id)/clearInterval(id);建议把所有定时器 ID 存进一个Set或数组,统一清理 - 避免在
setInterval回调里反复创建新函数(如setInterval(() => { doSomething(); }, 100)),每次都会生成新闭包;提取为具名函数复用 -
Promise链未处理拒绝状态(.catch缺失)不会导致内存泄漏,但未处理的unhandledrejection事件可能掩盖真实问题,间接影响调试效率
频繁读写 DOM 就得批量 + 脱离文档流
每轮 JS 执行中触发多次 offsetTop、getBoundingClientRect 或修改 style,都会强制浏览器同步回流(reflow),代价远高于 JS 运行本身。
立即学习“Java免费学习笔记(深入)”;
- 读取布局信息前,先用
documentFragment或display: none把元素移出渲染树,批量改完再挂回去 - 用
getComputedStyle替代反复读取element.style.xxx,后者只返回内联样式,易误判;且getComputedStyle结果可缓存复用 - 动画场景优先用
transform和opacity,它们走合成层(compositor layer),不触发布局计算;避免改top、left、width











