JavaScript 性能优化技巧
代码执行优化
1. 避免全局变量查找
javascript
// 慢
function slow() {
for (let i = 0; i < array.length; i++) {
document.getElementById("element").innerHTML += array[i];
}
}
// 快
function fast() {
const element = document.getElementById("element");
const length = array.length;
let html = "";
for (let i = 0; i < length; i++) {
html += array[i];
}
element.innerHTML = html;
}2. 使用局部变量缓存
javascript
// 慢
function slow() {
return this.data.value1 + this.data.value2 + this.data.value3;
}
// 快
function fast() {
const data = this.data;
return data.value1 + data.value2 + data.value3;
}3. 减少 DOM 操作
javascript
// 慢:多次 DOM 操作
function slow() {
const list = document.getElementById("list");
for (let i = 0; i < 1000; i++) {
const item = document.createElement("li");
item.textContent = `Item ${i}`;
list.appendChild(item);
}
}
// 快:批量 DOM 操作
function fast() {
const list = document.getElementById("list");
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const item = document.createElement("li");
item.textContent = `Item ${i}`;
fragment.appendChild(item);
}
list.appendChild(fragment);
}4. 使用事件委托
javascript
// 慢:为每个元素绑定事件
function slow() {
const items = document.querySelectorAll(".item");
items.forEach(item => {
item.addEventListener("click", handleClick);
});
}
// 快:事件委托
function fast() {
const container = document.getElementById("container");
container.addEventListener("click", function(e) {
if (e.target.classList.contains("item")) {
handleClick(e);
}
});
}循环优化
1. 减少循环中的计算
javascript
// 慢
for (let i = 0; i < array.length; i++) {
// 每次循环都计算 array.length
}
// 快
for (let i = 0, len = array.length; i < len; i++) {
// 只计算一次长度
}
// 更快:倒序循环
for (let i = array.length; i--;) {
// 倒序循环,减少比较操作
}2. 使用高效的循环方法
javascript
const array = [1, 2, 3, 4, 5];
// for 循环最快
for (let i = 0; i < array.length; i++) {
// 处理
}
// for...of 次之
for (const item of array) {
// 处理
}
// 数组方法较慢但更易读
array.forEach(item => {
// 处理
});3. 提前退出循环
javascript
// 使用 break 或 return 提前退出
function findItem(array, target) {
for (let i = 0; i < array.length; i++) {
if (array[i] === target) {
return i; // 找到后立即返回
}
}
return -1;
}函数优化
1. 函数节流(Throttle)
javascript
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 使用
const handleScroll = throttle(() => {
console.log("滚动事件");
}, 1000);
window.addEventListener("scroll", handleScroll);2. 函数防抖(Debounce)
javascript
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
// 使用
const handleSearch = debounce((query) => {
console.log("搜索:", query);
}, 300);
input.addEventListener("input", (e) => {
handleSearch(e.target.value);
});3. 函数记忆化(Memoization)
javascript
function memoize(func) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (cache[key]) {
return cache[key];
}
const result = func.apply(this, args);
cache[key] = result;
return result;
};
}
// 使用
const fibonacci = memoize(function(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
console.log(fibonacci(40)); // 快速计算内存管理
1. 避免内存泄漏
javascript
// 内存泄漏示例
function leak() {
const largeData = new Array(1000000).fill("data");
document.getElementById("button").addEventListener("click", function() {
// 闭包持有 largeData 引用
console.log("clicked");
});
}
// 修复
function fixed() {
document.getElementById("button").addEventListener("click", function() {
console.log("clicked");
});
// 不再持有 largeData 引用
}2. 及时清理事件监听器
javascript
class Component {
constructor() {
this.handleResize = this.handleResize.bind(this);
window.addEventListener("resize", this.handleResize);
}
handleResize() {
// 处理逻辑
}
destroy() {
// 清理事件监听器
window.removeEventListener("resize", this.handleResize);
}
}3. 使用 WeakMap 和 WeakSet
javascript
// WeakMap 允许垃圾回收
const weakMap = new WeakMap();
let obj = { data: "large data" };
weakMap.set(obj, "metadata");
obj = null; // obj 可以被垃圾回收,WeakMap 中的条目也会被清除算法优化
1. 选择合适的数据结构
javascript
// 查找操作:Set 比 Array 快
const array = [1, 2, 3, 4, 5];
console.log(array.includes(3)); // O(n)
const set = new Set([1, 2, 3, 4, 5]);
console.log(set.has(3)); // O(1)
// 键值对操作:Map 比 Object 快(大量数据时)
const map = new Map();
map.set("key", "value");
console.log(map.get("key"));2. 使用二分查找
javascript
function binarySearch(array, target) {
let left = 0;
let right = array.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (array[mid] === target) {
return mid;
} else if (array[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}3. 避免不必要的计算
javascript
// 慢:重复计算
function slow(n) {
for (let i = 0; i < n; i++) {
const result = expensiveCalculation(i);
if (result > 100) {
return result;
}
}
}
// 快:缓存结果
function fast(n) {
const cache = new Map();
for (let i = 0; i < n; i++) {
let result;
if (cache.has(i)) {
result = cache.get(i);
} else {
result = expensiveCalculation(i);
cache.set(i, result);
}
if (result > 100) {
return result;
}
}
}异步优化
1. 使用 Promise.all 并行执行
javascript
// 慢:串行执行
async function slow() {
const result1 = await fetchData1();
const result2 = await fetchData2();
const result3 = await fetchData3();
return [result1, result2, result3];
}
// 快:并行执行
async function fast() {
const [result1, result2, result3] = await Promise.all([
fetchData1(),
fetchData2(),
fetchData3()
]);
return [result1, result2, result3];
}2. 使用 Web Workers
javascript
// 主线程
const worker = new Worker("worker.js");
worker.postMessage({ data: largeData });
worker.onmessage = function(e) {
console.log("结果:", e.data);
};
// worker.js
self.onmessage = function(e) {
const result = heavyComputation(e.data.data);
self.postMessage(result);
};代码分割和懒加载
1. 动态导入
javascript
// 懒加载模块
async function loadModule() {
const module = await import("./heavy-module.js");
module.doSomething();
}
// 条件加载
if (condition) {
const module = await import("./module-a.js");
} else {
const module = await import("./module-b.js");
}2. 图片懒加载
javascript
const images = document.querySelectorAll("img[data-src]");
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
imageObserver.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));总结
性能优化要点:
- 减少 DOM 操作:批量操作,使用 DocumentFragment
- 优化循环:缓存长度,提前退出
- 函数优化:节流、防抖、记忆化
- 内存管理:避免泄漏,及时清理
- 算法优化:选择合适的数据结构
- 异步优化:并行执行,使用 Web Workers