Skip to content

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