JavaScript 的性能优化可以从多个层面入手,涵盖代码执行效率、内存管理、DOM 操作、网络请求等。以下是一些关键优化策略:
一、代码执行优化
减少作用域链查找
- 避免在循环中频繁访问全局变量或深层嵌套的属性,将其缓存到局部变量中。
// 优化前 for (let i = 0; i < array.length; i++) { /* ... */ } // 优化后 const len = array.length; for (let i = 0; i < len; i++) { /* ... */ }
选择高效的数据结构和方法
- 优先使用
Map
/Set
(适合频繁增删或键值查找),而非普通对象/数组。 - 使用
Array.prototype.includes
替代indexOf
(更语义化)。
- 优先使用
避免阻塞主线程
- 将耗时任务拆分到 Web Workers(后台线程)。
- 使用
requestIdleCallback
或requestAnimationFrame
调度非关键任务。
防抖(Debounce)与节流(Throttle)
- 防抖:高频事件(如
resize
/input
)停止触发后再执行(例如搜索框联想)。 - 节流:固定时间间隔执行一次(例如滚动事件处理)。
function debounce(fn, delay) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; }
- 防抖:高频事件(如
减少闭包滥用
- 闭包可能导致内存泄漏(变量被长期引用无法释放)。
二、DOM 操作优化
减少 DOM 访问次数
- 缓存 DOM 查询结果,避免重复查询。
// 优化前 for (let i = 0; i < 100; i++) { document.getElementById('element').style.left = i + 'px'; } // 优化后 const element = document.getElementById('element'); for (let i = 0; i < 100; i++) { element.style.left = i + 'px'; }
批量修改 DOM
- 使用
document.createDocumentFragment()
或innerHTML
批量插入元素。 - 使用
requestAnimationFrame
合并样式修改,减少重排(Reflow)和重绘(Repaint)。
- 使用
使用事件委托
- 通过事件冒泡在父元素上统一处理子元素事件,减少监听器数量。
document.getElementById('parent').addEventListener('click', (e) => { if (e.target.matches('.child')) { // 处理子元素点击事件 } });
优化 CSS 选择器
- 避免过于复杂的选择器(如
div:nth-child(3) > a.highlight
),增加样式计算时间。
- 避免过于复杂的选择器(如
三、网络与加载优化
代码拆分与懒加载
- 使用 Webpack 等工具的
dynamic import()
实现按需加载。
// 按需加载模块 button.onclick = async () => { const module = await import('./heavyModule.js'); module.doSomething(); };
- 使用 Webpack 等工具的
压缩与合并资源
- 使用工具(如 Terser)压缩 JS 代码,移除未使用的代码(Tree Shaking)。
- 合并小文件,减少 HTTP 请求次数。
预加载关键资源
- 使用
<link rel="preload">
提前加载关键脚本或字体。
- 使用
启用 HTTP 缓存
- 设置
Cache-Control
、ETag
等响应头,利用浏览器缓存。
- 设置
四、内存管理
避免内存泄漏
- 及时解绑事件监听器(尤其是单页应用中的全局事件)。
- 清除不再使用的定时器(
setInterval
/setTimeout
)。 - 避免意外的全局变量(未声明的变量会挂载到
window
)。
function leak() { leakedVar = 'This is a global variable'; // 意外的全局变量! }
释放不再需要的引用
- 手动将大对象(如数组、DOM 元素)设为
null
,提示垃圾回收器(GC)回收。
let data = fetchHugeData(); process(data); data = null; // 主动释放引用
- 手动将大对象(如数组、DOM 元素)设为
使用弱引用
- 用
WeakMap
/WeakSet
存储临时数据,避免阻止 GC 回收键对象。
- 用
五、工具与监控
性能分析工具
- Chrome DevTools 的 Performance 面板分析运行时性能,Memory 面板追踪内存泄漏。
- Lighthouse 评估整体性能并给出优化建议。
代码质量工具
- 使用 ESLint 检查低效代码模式(如循环内创建函数)。
实时监控
- 使用 PerformanceObserver API 监控长任务、卡顿等。
const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { console.log('Long task:', entry); } }); observer.observe({ entryTypes: ['longtask'] });
六、其他优化技巧
使用 CSS 动画替代 JS 动画
- CSS 动画(
transform
/opacity
)通常由 GPU 加速,性能更优。
- CSS 动画(
减少微任务(Microtasks)堆积
- 避免在循环中频繁触发
Promise.resolve()
或MutationObserver
。
- 避免在循环中频繁触发
优化递归算法
- 将递归改为循环或尾递归(部分引擎支持尾调用优化)。
实际场景示例
- 无限滚动列表:使用虚拟滚动(仅渲染可视区域 DOM)。
- 搜索框联想:防抖 + 缓存已请求的结果。
- 复杂计算:移至 Web Worker 或服务端。
通过以上策略,结合具体场景分析瓶颈(如使用 Performance 工具),可以显著提升 JavaScript 应用的性能。