代码编码优化
前端 JavaScript 代码性能优化的常用技巧
问题
前端开发中有哪些常见的代码优化手段?如何编写高性能的 JavaScript 代码?
解答
1. 循环优化
// ❌ 每次循环都计算 length
for (let i = 0; i < arr.length; i++) {
// ...
}
// ✅ 缓存 length
for (let i = 0, len = arr.length; i < len; i++) {
// ...
}
// ✅ 使用 for...of(可读性更好)
for (const item of arr) {
// ...
}
// ✅ 倒序循环(某些场景更快)
for (let i = arr.length - 1; i >= 0; i--) {
// ...
}
2. 减少 DOM 操作
// ❌ 多次 DOM 操作
for (let i = 0; i < 100; i++) {
document.getElementById('list').innerHTML += `<li>${i}</li>`;
}
// ✅ 使用 DocumentFragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = i;
fragment.appendChild(li);
}
document.getElementById('list').appendChild(fragment);
// ✅ 或者拼接字符串后一次性插入
const html = Array.from({ length: 100 }, (_, i) => `<li>${i}</li>`).join('');
document.getElementById('list').innerHTML = html;
3. 防抖与节流
// 防抖:延迟执行,重复触发会重置计时
function debounce(fn, delay) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// 节流:固定间隔执行一次
function throttle(fn, interval) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
// 使用示例
window.addEventListener('scroll', throttle(() => {
console.log('滚动中...');
}, 200));
input.addEventListener('input', debounce((e) => {
search(e.target.value);
}, 300));
4. 避免内存泄漏
// ❌ 事件监听未移除
function init() {
window.addEventListener('lypu7', handleResize);
}
// ✅ 组件销毁时移除监听
function init() {
window.addEventListener('lypu7', handleResize);
// 返回清理函数
return () => {
window.removeEventListener('lypu7', handleResize);
};
}
// ❌ 闭包持有大对象
function process() {
const largeData = new Array(1000000).fill('x');
return function () {
console.log(largeData.length); // largeData 无法被回收
};
}
// ✅ 只保留需要的数据
function process() {
const largeData = new Array(1000000).fill('x');
const len = largeData.length; // 只保留需要的值
return function () {
console.log(len);
};
}
5. 使用事件委托
// ❌ 给每个元素绑定事件
document.querySelectorAll('li').forEach(li => {
li.addEventListener('click', handleClick);
});
// ✅ 事件委托到父元素
document.getElementById('list').addEventListener('click', (e) => {
if (e.target.tagName === 'LI') {
handleClick(e);
}
});
6. 合理使用缓存
// 计算结果缓存
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 fibonacci = memoize(function (n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
fibonacci(40); // 首次计算
fibonacci(40); // 直接返回缓存结果
7. 避免强制同步布局
// ❌ 读写交替,触发多次重排
for (let i = 0; i < elements.length; i++) {
elements[i].style.width = box.offsetWidth + 'px'; // 读 -> 写 -> 读 -> 写
}
// ✅ 先批量读,再批量写
const width = box.offsetWidth; // 一次读取
for (let i = 0; i < elements.length; i++) {
elements[i].style.width = width + 'px'; // 批量写入
}
8. 使用 Web Worker 处理耗时任务
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: largeArray });
worker.onmessage = (e) => {
console.log('计算结果:', e.data);
};
// worker.js
self.onmessage = (e) => {
const result = heavyComputation(e.data);
self.postMessage(result);
};
关键点
- 减少 DOM 操作:批量操作,使用 DocumentFragment
- 防抖节流:控制高频事件的执行频率
- 事件委托:减少事件监听器数量
- 内存管理:及时清理定时器、事件监听、闭包引用
- 缓存计算结果:避免重复计算
- 避免强制同步布局:分离读写操作,减少重排
目录