JavaScript性能优化聚焦函数执行、DOM更新、内存泄漏三大瓶颈:用requestIdleCallback替代高频setTimeout/setInterval,throttle/debounce滚动监听,避免闭包持有大对象,用DocumentFragment批量DOM操作。
J

setTimeout 和 setInterval 的高频轮询很多轮询逻辑(如轮询接口状态、监听滚动偏移)用 setTimeout 每 10ms 调一次,结果主线程长期被抢占,页面响应变钝。浏览器每秒重绘上限约 60 帧,对应间隔至少 16ms;低于这个值,纯属自造压力。
requestIdleCallback 处理低优先级任务(如日志上报、非关键状态同步)throttle 或 debounce 封装,阈值设为 16 或 32 ms,而非固定 10
setInterval 回调里做 DOM 查询或 JSON.parse —— 这些操作成本远高于定时器本身闭包本身不慢,但若内部函数被长期保留(比如绑定到事件、存入全局 Map、作为 Promise 回调),它捕获的外层作用域变量就无法被 GC 回收。常见陷阱是把整个 response.data 或 document.querySelector('.list') 塞进闭包。
function loadData() {
const bigData = await fetch('/api/items').then(r => r.json());
const handler = () => console.log(bigData.length); // ❌ 捕获了 bigData
document.addEventListener('click', handler);
// ✅ 正确做法:只传需要的字段
const len = bigData.length;
const safeHandler = () => console.log(len);
}WeakMap 存储实例私有数据,避免强引用阻碍回收Memory > Heap Snapshot,筛选 Closure 类型,看哪些闭包占用了 MB 级内存documentFragment 批量操作 DOM,而不是反复 appendChild
每次调用 element.appendChild(child) 都可能触发重排(reflow),尤其在循环中。100 次追加 = 100 次潜在重排。浏览器不会自动合并这些操作。
document.createDocumentFragment() 作为中转容器,一次性 append 到真实 DOMinnerHTML 字符串拼接(注意 XSS 风险)或 template + cloneNode(true)
Virtual Scrolling(如 react-window),而非渲染全部项真正卡住页面的,往往不是某行代码多慢,而是多个小问题叠加:一个没清理的定时器 + 一个闭包里的大数组 + 每次都重排的 DOM 插入。查性能问题,先开 Chrome 的 Performance 面板录一段用户操作,重点看 Scripting 和 Rendering 时间占比,再针对性切片分析 —— 不要猜,要看。