JavaScript 性能优化最佳实践
中等 🟡Js/Ts
4 个标签
预计阅读时间:13 分钟
JavaScript性能优化内存执行速度
JavaScript 性能优化最佳实践
JavaScript 性能直接影响应用的用户体验。一个高性能的 JavaScript 应用应该具备快速响应、流畅动画、低内存占用等特点。性能优化是一个持续的过程,需要在开发的各个阶段关注。以下是一些关键的性能优化策略,涵盖代码优化、内存管理、异步处理、DOM 操作等多个方面。
代码优化
变量声明与作用域:
•使用 let 和 const 代替 var,利用块级作用域避免变量提升问题
•减少全局变量的使用,全局变量会污染全局命名空间,增加查找时间
•合理使用闭包,闭包会保持对外部变量的引用,可能导致内存泄漏
javascriptCode
// ❌ 避免:全局变量污染
var globalVar = 'global';
// ✅ 推荐:使用模块作用域
const Module = (() => {
const privateVar = 'private';
return {
getPrivate: () => privateVar,
};
})();
// ❌ 避免:闭包导致的内存问题
function createHandlers() {
const handlers = [];
for (var i = 0; i < 10; i++) {
handlers.push(function() {
console.log(i); // 所有函数都输出 10
});
}
return handlers;
}
// ✅ 推荐:使用 let 或 IIFE
function createHandlers() {
const handlers = [];
for (let i = 0; i < 10; i++) {
handlers.push(() => console.log(i)); // 输出 0-9
}
return handlers;
}函数优化:
•避免在循环中定义函数,每次迭代都会创建新的函数对象
•使用箭头函数简化代码,但注意 this 绑定问题
•合理使用函数柯里化,实现函数复用和延迟计算
javascriptCode
// ❌ 避免:循环中创建函数
const handlers = [];
for (let i = 0; i < 1000; i++) {
handlers.push(function() {
return i * 2;
});
}
// ✅ 推荐:提取函数
const double = (i) => i * 2;
const handlers = [];
for (let i = 0; i < 1000; i++) {
handlers.push(() => double(i));
}
// 函数柯里化优化
const curry = (fn) => {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return (...moreArgs) => curried.apply(this, args.concat(moreArgs));
};
};
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
const add5 = curriedAdd(5); // 部分应用,复用函数循环优化:
•使用 for 循环代替 forEach,for 循环性能更好
•缓存数组长度,避免每次迭代都计算
•避免在循环中进行 DOM 操作,使用 DocumentFragment 批量操作
javascriptCode
// ❌ 避免:每次迭代都计算长度
for (let i = 0; i < array.length; i++) {
// ...
}
// ✅ 推荐:缓存数组长度
for (let i = 0, len = array.length; i < len; i++) {
// ...
}
// ❌ 避免:循环中进行 DOM 操作
for (let i = 0; i < items.length; i++) {
const li = document.createElement('li');
li.textContent = items[i];
list.appendChild(li); // 每次都触发重排
}
// ✅ 推荐:使用 DocumentFragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < items.length; i++) {
const li = document.createElement('li');
li.textContent = items[i];
fragment.appendChild(li);
}
list.appendChild(fragment); // 只触发一次重排条件优化:
•使用 switch 代替多个 if-else,switch 使用跳转表优化
•将最常见的条件放在前面,减少判断次数
•使用对象字面量代替条件判断,提高可读性和性能
javascriptCode
// ❌ 避免:多个 if-else
function getStatus(status) {
if (status === 'pending') return '等待中';
else if (status === 'processing') return '处理中';
else if (status === 'completed') return '已完成';
else if (status === 'failed') return '失败';
else return '未知';
}
// ✅ 推荐:使用对象字面量
const statusMap = {
pending: '等待中',
processing: '处理中',
completed: '已完成',
failed: '失败',
};
function getStatus(status) {
return statusMap[status] || '未知';
}
// ✅ 推荐:使用 Map(更高效)
const statusMap = new Map([
['pending', '等待中'],
['processing', '处理中'],
['completed', '已完成'],
['failed', '失败'],
]);内存管理
内存泄漏常见原因与预防:
•避免循环引用,特别是 DOM 元素与 JavaScript 对象之间的引用
•清理定时器和事件监听器,组件销毁时必须清理
•释放不再使用的对象引用,帮助垃圾回收器回收内存
javascriptCode
// ❌ 避免:未清理的定时器
class Component {
constructor() {
this.timer = setInterval(() => {
this.updateData();
}, 1000);
}
// 缺少清理逻辑
}
// ✅ 推荐:正确清理
class Component {
constructor() {
this.timer = setInterval(() => {
this.updateData();
}, 1000);
}
destroy() {
clearInterval(this.timer);
}
}
// ❌ 避免:未移除的事件监听器
class Modal {
constructor() {
document.addEventListener('keydown', this.handleKeydown);
}
}
// ✅ 推荐:正确移除监听器
class Modal {
constructor() {
this.handleKeydown = this.handleKeydown.bind(this);
document.addEventListener('keydown', this.handleKeydown);
}
destroy() {
document.removeEventListener('keydown', this.handleKeydown);
}
}
// ❌ 避免:闭包持有大对象引用
function createProcessor(largeData) {
return function process() {
// 即使只用一部分数据,整个 largeData 都被引用
return largeData[0];
};
}
// ✅ 推荐:只保留需要的数据
function createProcessor(largeData) {
const neededData = largeData[0];
return function process() {
return neededData;
};
}垃圾回收机制理解:
•JavaScript 使用标记清除算法进行垃圾回收
•避免创建过多的临时对象,减少 GC 压力
•使用 WeakMap 和 WeakSet 存储对象引用,不阻止垃圾回收
javascriptCode
// WeakMap 示例:不阻止垃圾回收
const privateData = new WeakMap();
class MyClass {
constructor() {
privateData.set(this, { secret: 'value' });
}
getSecret() {
return privateData.get(this)?.secret;
}
}
// 当 MyClass 实例被销毁时,WeakMap 中的数据也会被回收
// WeakSet 示例:跟踪对象而不阻止回收
const visitedObjects = new WeakSet();
function processObject(obj) {
if (visitedObjects.has(obj)) {
return; // 已处理过
}
visitedObjects.add(obj);
// 处理对象
}内存优化技巧:
•使用对象池复用对象,减少对象创建和销毁的开销
•合理使用缓存,但要注意缓存大小和过期策略
•减少内存分配,重用数组和对象
javascriptCode
// 对象池模式
class ObjectPool {
constructor(factory, initialSize = 10) {
this.factory = factory;
this.pool = [];
for (let i = 0; i < initialSize; i++) {
this.pool.push(factory());
}
}
acquire() {
return this.pool.length > 0 ? this.pool.pop() : this.factory();
}
release(obj) {
// 重置对象状态
Object.keys(obj).forEach(key => delete obj[key]);
this.pool.push(obj);
}
}
// 使用示例
const vectorPool = new ObjectPool(() => ({ x: 0, y: 0, z: 0 }));
function calculate() {
const v = vectorPool.acquire();
// 使用 v 进行计算
vectorPool.release(v);
}
// 数组复用
const tempArray = [];
function processItems(items) {
tempArray.length = 0; // 清空但保留内存
for (const item of items) {
if (item.active) {
tempArray.push(item);
}
}
return tempArray;
}异步优化
异步操作:
•使用 Promise 和 async/await
•避免回调地狱
•合理控制并发数量
网络请求:
•合并请求
•使用缓存
•实现请求节流
资源加载:
•延迟加载非关键资源
•预加载关键资源
•使用 CDN
浏览器优化
DOM 操作:
•减少 DOM 操作次数
•使用 DocumentFragment
•批量更新 DOM
事件处理:
•使用事件委托
•避免过多的事件监听器
•清理事件监听器
渲染优化:
•避免重排和重绘
•使用 CSS 动画代替 JavaScript 动画
•合理使用 requestAnimationFrame
工具和监控
性能分析:
•使用 Chrome DevTools
•分析执行时间
•识别性能瓶颈
监控工具:
•Lighthouse
•Web Vitals
•自定义性能监控
构建优化:
•代码压缩
•代码分割
• tree-shaking
最佳实践
•编写高效的算法
•合理使用数据结构
•避免不必要的计算
•优化关键路径
•定期性能测试
•持续优化
•学习性能优化技巧
•关注性能最佳实践