Appearance
JavaScript 性能优化
JavaScript性能优化是构建高效Web应用的关键。通过优化代码,可以提升用户体验,减少资源消耗,提高应用响应速度。
性能分析工具
浏览器开发者工具
javascript
// 使用Performance API测量执行时间
console.time('operation');
// 执行操作
for (let i = 0; i < 1000000; i++) {
// 某些操作
}
console.timeEnd('operation');
// 使用Performance API进行更精确的测量
const start = performance.now();
// 执行操作
const end = performance.now();
console.log(`操作耗时: ${end - start} 毫秒`);
使用console.profile进行性能分析
javascript
// 开始性能分析
console.profile('MyProfile');
// 执行需要分析的代码
function intensiveOperation() {
// 一些密集操作
const arr = [];
for (let i = 0; i < 100000; i++) {
arr.push(i * 2);
}
return arr;
}
intensiveOperation();
// 结束性能分析
console.profileEnd('MyProfile');
内存优化
避免内存泄漏
javascript
// 1. 及时清理事件监听器
class Component {
constructor(element) {
this.element = element;
this.handleClick = this.handleClick.bind(this);
this.element.addEventListener('click', this.handleClick);
}
handleClick() {
console.log('Clicked!');
}
destroy() {
// 移除事件监听器
this.element.removeEventListener('click', this.handleClick);
// 清理引用
this.element = null;
this.handleClick = null;
}
}
// 2. 避免循环引用
// 不好的做法
const parent = {};
const child = { parent: parent };
parent.child = child; // 循环引用
// 好的做法
const parent = {};
const child = { parentId: parent.id }; // 使用ID引用而不是对象引用
// 3. 清理定时器
class Timer {
constructor() {
this.intervalId = setInterval(() => {
console.log('tick');
}, 1000);
}
stop() {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
优化数组操作
javascript
// 1. 避免在循环中创建数组
// 不好的做法
function badExample(items) {
const results = [];
for (let i = 0; i < items.length; i++) {
results.push(items[i] * 2);
}
return results;
}
// 好的做法 - 使用map
function goodExample(items) {
return items.map(item => item * 2);
}
// 2. 预分配数组大小(当大小已知时)
// 不好的做法
function createArray(n) {
const arr = [];
for (let i = 0; i < n; i++) {
arr.push(null);
}
return arr;
}
// 好的做法
function createArray(n) {
return new Array(n).fill(null);
}
// 3. 使用适当的数组方法
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 对于查找,使用find而不是filter(当只需要第一个匹配项时)
const firstEven = numbers.find(n => n % 2 === 0); // 返回5
// 而不是
// const firstEven = numbers.filter(n => n % 2 === 0)[0]; // 创建了整个过滤数组
// 对于存在性检查,使用some而不是filter
const hasEven = numbers.some(n => n % 2 === 0); // 返回true/false
算法优化
选择合适的数据结构
javascript
// 1. 使用Map而不是Object进行键值对存储(当键不是字符串时)
// 不好的做法
const obj = {};
obj[someObject] = 'value'; // 对象会被转换为字符串'[object Object]'
// 好的做法
const map = new Map();
map.set(someObject, 'value');
// 2. 使用Set进行唯一值存储
// 不好的做法
function removeDuplicates(arr) {
const unique = [];
for (const item of arr) {
if (unique.indexOf(item) === -1) {
unique.push(item);
}
}
return unique;
}
// 好的做法
function removeDuplicates(arr) {
return [...new Set(arr)];
}
// 3. 使用Object的键进行快速查找
const validStatuses = ['active', 'inactive', 'pending'];
const status = 'active';
// 不好的做法 - O(n)时间复杂度
if (validStatuses.includes(status)) {
// 处理
}
// 好的做法 - O(1)时间复杂度
const validStatusesSet = new Set(validStatuses);
if (validStatusesSet.has(status)) {
// 处理
}
// 或者使用对象
const validStatusesObj = { active: true, inactive: true, pending: true };
if (validStatusesObj[status]) {
// 处理
}
优化循环
javascript
// 1. 缓存数组长度
const items = new Array(1000000).fill(1);
// 不好的做法
for (let i = 0; i < items.length; i++) {
// 每次都要访问items.length
}
// 好的做法
for (let i = 0, len = items.length; i < len; i++) {
// 缓存了长度
}
// 2. 使用for...of而不是forEach(在某些情况下)
// forEach有函数调用开销
for (const item of items) {
// 处理item
}
// 3. 避免不必要的计算
// 不好的做法
for (let i = 0; i < items.length; i++) {
const processed = expensiveOperation(items[i]);
if (processed > threshold) {
break; // 但仍然计算了expensiveOperation
}
}
// 好的做法
for (let i = 0; i < items.length; i++) {
if (someQuickCheck(items[i])) {
const processed = expensiveOperation(items[i]);
if (processed > threshold) {
break;
}
}
}
函数优化
避免重复计算
javascript
// 1. 使用缓存/记忆化
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
const expensiveCalculation = memoize((n) => {
// 模拟耗时计算
let result = 0;
for (let i = 0; i < n; i++) {
result += i * i;
}
return result;
});
// 2. 避免在函数内部创建函数(在循环中)
// 不好的做法
function createHandlers(items) {
return items.map(item => {
return function() {
// 每次都创建新函数
console.log(item.value);
};
});
}
// 好的做法
function createHandlers(items) {
const handler = function(item) {
return function() {
console.log(item.value);
};
};
return items.map(item => handler(item));
}
// 3. 使用箭头函数时注意this绑定
class Calculator {
constructor() {
this.result = 0;
}
// 不好的做法 - this指向问题
processArrayBad(arr) {
arr.forEach(function(item) {
this.result += item; // this不指向Calculator实例
});
}
// 好的做法
processArrayGood(arr) {
arr.forEach(item => {
this.result += item; // 箭头函数继承this
});
}
// 或者预先绑定
processArrayAlsoGood(arr) {
const adder = function(item) {
this.result += item;
}.bind(this);
arr.forEach(adder);
}
}
DOM 操作优化
减少重排和重绘
javascript
// 1. 批量DOM操作
const container = document.getElementById('container');
// 不好的做法 - 多次重排重绘
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
div.style.width = '100px';
div.style.height = '50px';
container.appendChild(div);
// 每次appendChild都会可能触发重排
}
// 好的做法 - 使用文档片段
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
div.style.width = '100px';
div.style.height = '50px';
fragment.appendChild(div);
}
container.appendChild(fragment); // 只触发一次重排
// 2. 避免强制同步布局
const element = document.getElementById('myElement');
// 不好的做法 - 强制同步布局
element.style.left = '10px';
console.log(element.offsetLeft); // 强制浏览器立即计算布局
element.style.top = '10px';
console.log(element.offsetTop); // 再次强制布局计算
// 好的做法 - 批量读写操作
element.style.left = '10px';
element.style.top = '10px';
const left = element.offsetLeft;
const top = element.offsetTop;
// 3. 使用CSS类而不是直接操作样式
// 不好的做法
element.style.color = 'red';
element.style.backgroundColor = 'blue';
element.style.fontSize = '16px';
// 好的做法
element.className = 'highlighted';
// CSS:
// .highlighted {
// color: red;
// background-color: blue;
// font-size: 16px;
// }
事件优化
javascript
// 1. 使用事件委托
// 不好的做法 - 为每个元素添加事件监听器
document.querySelectorAll('.button').forEach(button => {
button.addEventListener('click', handleClick);
});
// 好的做法 - 事件委托
document.addEventListener('click', function(event) {
if (event.target.matches('.button')) {
handleClick(event);
}
});
// 2. 使用被动事件监听器
element.addEventListener('touchstart', handleTouch, { passive: true });
// 3. 限制事件处理器的执行频率
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
}
}
// 用于滚动事件
const throttledScroll = throttle(function() {
console.log('处理滚动事件');
}, 100);
window.addEventListener('scroll', throttledScroll);
// 防抖用于搜索输入
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
}
}
const debouncedSearch = debounce(function(event) {
performSearch(event.target.value);
}, 300);
document.getElementById('search').addEventListener('input', debouncedSearch);
异步操作优化
Promise 优化
javascript
// 1. 避免Promise链过长
// 不好的做法
fetchUser()
.then(user => fetchUserPosts(user.id))
.then(posts => fetchUserComments(posts))
.then(comments => processComments(comments))
.then(result => displayResult(result))
.catch(error => console.error(error));
// 好的做法 - 使用async/await
async function loadUserData() {
try {
const user = await fetchUser();
const posts = await fetchUserPosts(user.id);
const comments = await fetchUserComments(posts);
const result = await processComments(comments);
return displayResult(result);
} catch (error) {
console.error(error);
}
}
// 2. 并行执行独立的异步操作
// 不好的做法 - 串行执行
async function badParallel() {
const user = await fetchUser(1);
const posts = await fetchUser(2);
const comments = await fetchUser(3);
return { user, posts, comments };
}
// 好的做法 - 并行执行
async function goodParallel() {
const [user, posts, comments] = await Promise.all([
fetchUser(1),
fetchUser(2),
fetchUser(3)
]);
return { user, posts, comments };
}
// 3. 处理可能部分失败的并行操作
async function handlePartialFailure() {
const results = await Promise.allSettled([
fetchUser(1),
fetchUser(2),
fetchUser(3)
]);
const data = {};
results.forEach((result, index) => {
const keys = ['user', 'posts', 'comments'];
if (result.status === 'fulfilled') {
data[keys[index]] = result.value;
} else {
console.error(`${keys[index]} 获取失败:`, result.reason);
data[keys[index]] = null;
}
});
return data;
}
代码分割和懒加载
动态导入优化
javascript
// 1. 按需加载功能模块
async function loadChartingLibrary() {
// 只在需要图表功能时才加载
const { Chart } = await import('chart.js');
return new Chart();
}
// 2. 路由级别的代码分割
async function loadRoute(routeName) {
const routes = {
'dashboard': () => import('./Dashboard.js'),
'profile': () => import('./Profile.js'),
'settings': () => import('./Settings.js')
};
if (routes[routeName]) {
const module = await routes[routeName]();
return module.default;
}
throw new Error(`Unknown route: ${routeName}`);
}
// 3. 预加载重要模块
const essentialModules = Promise.all([
import('./router.js'),
import('./auth.js'),
import('./api.js')
]);
// 4. 条件加载
async function initializeFeature() {
if (window.location.pathname === '/admin') {
const adminModule = await import('./admin-panel.js');
adminModule.init();
}
}
优化技巧和模式
对象优化
javascript
// 1. 避免在循环中创建对象
// 不好的做法
function processItems(items) {
return items.map(item => {
return {
id: item.id,
name: item.name,
processed: true,
timestamp: Date.now()
};
});
}
// 好的做法 - 重用对象结构
function processItems(items) {
const template = {
id: null,
name: null,
processed: true,
timestamp: Date.now()
};
return items.map(item => {
return {
...template,
id: item.id,
name: item.name
};
});
}
// 2. 使用对象池减少垃圾回收
class ObjectPool {
constructor(createFn, resetFn) {
this.createFn = createFn;
this.resetFn = resetFn;
this.pool = [];
}
acquire() {
return this.pool.length > 0 ? this.pool.pop() : this.createFn();
}
release(obj) {
this.resetFn(obj);
this.pool.push(obj);
}
}
// 使用示例
const vectorPool = new ObjectPool(
() => ({ x: 0, y: 0 }),
(obj) => { obj.x = 0; obj.y = 0; }
);
function useVector() {
const vector = vectorPool.acquire();
vector.x = 10;
vector.y = 20;
// 使用vector...
vectorPool.release(vector);
}
字符串优化
javascript
// 1. 使用模板字符串而不是字符串连接
const name = 'John';
const age = 30;
// 不好的做法
const message = 'Hello, my name is ' + name + ' and I am ' + age + ' years old.';
// 好的做法
const message = `Hello, my name is ${name} and I am ${age} years old.`;
// 2. 大量字符串拼接使用数组
// 不好的做法
let result = '';
for (let i = 0; i < 10000; i++) {
result += 'item' + i + ',';
}
// 好的做法
const items = [];
for (let i = 0; i < 10000; i++) {
items.push('item' + i);
}
const result = items.join(',');
// 3. 使用String.fromCharCode()处理字符码
const codes = [72, 101, 108, 108, 111]; // 'Hello'的字符码
const str = String.fromCharCode(...codes);
性能监控
性能测量
javascript
// 1. 使用Performance API
function measureFunction(fn, ...args) {
const start = performance.now();
const result = fn.apply(this, args);
const end = performance.now();
console.log(`${fn.name} 执行时间: ${end - start} 毫秒`);
return result;
}
// 2. 监控内存使用
function getMemoryInfo() {
if (performance.memory) {
return {
used: performance.memory.usedJSHeapSize,
total: performance.memory.totalJSHeapSize,
limit: performance.memory.jsHeapSizeLimit
};
}
return null;
}
// 3. 监控长任务
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) { // 超过50ms的任务
console.warn('长任务检测到:', entry);
}
}
});
observer.observe({ entryTypes: ['longtask'] });
// 4. 自定义性能标记
performance.mark('start-operation');
// 执行操作
performance.mark('end-operation');
performance.measure('operation', 'start-operation', 'end-operation');
const measures = performance.getEntriesByName('operation');
console.log('操作耗时:', measures[0].duration);
最佳实践总结
性能优化检查清单
javascript
// 1. 代码层面
// - 使用合适的数据结构
// - 避免不必要的计算
// - 使用缓存机制
// - 优化循环和条件判断
// 2. DOM操作层面
// - 减少DOM查询
// - 批量DOM操作
// - 使用事件委托
// - 避免强制同步布局
// 3. 异步操作层面
// - 合理使用Promise
// - 并行执行独立操作
// - 使用防抖和节流
// 4. 内存管理层面
// - 及时清理事件监听器
// - 避免内存泄漏
// - 使用对象池
// 5. 网络层面
// - 代码分割和懒加载
// - 使用CDN
// - 启用压缩
// 示例:优化的数据处理类
class OptimizedDataProcessor {
constructor() {
this.cache = new Map();
this.pool = [];
}
process(data) {
const cacheKey = JSON.stringify(data);
// 检查缓存
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
// 从池中获取处理对象
let processor = this.pool.pop() || this.createProcessor();
// 处理数据
const result = this.performProcessing(processor, data);
// 存入缓存
this.cache.set(cacheKey, result);
// 将处理器放回池中
this.resetProcessor(processor);
this.pool.push(processor);
return result;
}
createProcessor() {
return {
tempArray: new Array(100),
tempObj: {}
};
}
resetProcessor(processor) {
processor.tempArray.fill(0);
Object.keys(processor.tempObj).forEach(key => delete processor.tempObj[key]);
}
performProcessing(processor, data) {
// 实际的数据处理逻辑
return data.map(item => item * 2);
}
}
JavaScript性能优化是一个持续的过程,需要结合实际应用场景选择合适的优化策略。始终记住"过早优化是万恶之源",应该先测量性能瓶颈,再针对性地进行优化。