JavaScript性能优化实战指南:让你的代码飞起来!

为什么要关心JavaScript性能?

当用户点击你的网站,等了3秒还在转圈圈,他们会怎么办?没错,直接关闭页面!研究表明:

  • 页面加载超过3秒,53%的用户会离开
  • 每慢1秒,转化率下降7%
  • 性能差的网站,用户体验评分会显著下降

所以,JavaScript性能优化不是可选项,而是必需品!

第一章:找出性能瓶颈

1.1 使用浏览器开发者工具

首先,我们需要知道问题在哪里,才能对症下药。

Chrome DevTools 性能分析:

// 在代码中添加性能标记
console.time('数据处理');

// 你的代码逻辑
processLargeData();

console.timeEnd('数据处理');

// 更精确的性能测量
performance.mark('start-heavy-task');
heavyComputationTask();
performance.mark('end-heavy-task');
performance.measure('heavy-task-duration', 'start-heavy-task', 'end-heavy-task');

实用的性能监控函数:

class PerformanceMonitor {
  static measureFunction(fn, name = 'function') {
    return function(...args) {
      const start = performance.now();
      const result = fn.apply(this, args);
      const end = performance.now();
      console.log(`${name} 执行时间: ${(end - start).toFixed(2)}ms`);
      return result;
    };
  }
  
  static async measureAsync(asyncFn, name = 'async function') {
    const start = performance.now();
    const result = await asyncFn();
    const end = performance.now();
    console.log(`${name} 执行时间: ${(end - start).toFixed(2)}ms`);
    return result;
  }
}

// 使用示例
const optimizedSort = PerformanceMonitor.measureFunction(
  (arr) => arr.sort((a, b) => a - b), 
  '数组排序'
);

1.2 识别常见性能问题

// ❌ 性能杀手示例
function badPerformanceExample() {
  // 1. 频繁的DOM操作
  for (let i = 0; i < 1000; i++) {
    document.getElementById('container').innerHTML += `<div>${i}</div>`;
  }
  
  // 2. 内存泄漏
  const heavyObject = new Array(1000000).fill('data');
  window.globalLeak = heavyObject; // 永远不会被回收
  
  // 3. 低效的循环
  const items = document.querySelectorAll('.item');
  for (let i = 0; i < items.length; i++) {
    items[i].style.color = 'red';
    items[i].offsetHeight; // 强制重排
  }
}

// ✅ 优化后的版本
function goodPerformanceExample() {
  // 1. 批量DOM操作
  const container = document.getElementById('container');
  const fragment = document.createDocumentFragment();
  for (let i = 0; i < 1000; i++) {
    const div = document.createElement('div');
    div.textContent = i;
    fragment.appendChild(div);
  }
  container.appendChild(fragment);
  
  // 2. 适当的内存管理
  function processHeavyData() {
    const heavyObject = new Array(1000000).fill('data');
    // 使用完后自动回收
    return heavyObject.reduce((sum, item) => sum + item.length, 0);
  }
  
  // 3. 高效的样式更新
  const items = document.querySelectorAll('.item');
  // 使用 requestAnimationFrame 批处理
  requestAnimationFrame(() => {
    items.forEach(item => {
      item.style.color = 'red';
    });
  });
}

第二章:DOM操作优化

2.1 减少DOM查询次数

// ❌ 重复查询DOM
function inefficientDOMAccess() {
  document.getElementById('title').textContent = 'Hello';
  document.getElementById('title').style.color = 'blue';
  document.getElementById('title').classList.add('highlight');
}

// ✅ 缓存DOM引用
function efficientDOMAccess() {
  const title = document.getElementById('title');
  title.textContent = 'Hello';
  title.style.color = 'blue';
  title.classList.add('highlight');
}

// 更进一步:批量操作
function batchDOMOperations() {
  const title = document.getElementById('title');
  
  // 使用 DocumentFragment 批量插入
  if (needsMultipleChildren) {
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < 100; i++) {
      const child = document.createElement('span');
      child.textContent = `Item ${i}`;
      fragment.appendChild(child);
    }
    title.appendChild(fragment);
  }
  
  // 批量样式更新
  title.style.cssText = 'color: blue; font-size: 16px; font-weight: bold;';
}

2.2 虚拟滚动实现

当需要渲染大量列表项时,虚拟滚动是必备技能:

class VirtualScroller {
  constructor(container, items, itemHeight = 50) {
    this.container = container;
    this.items = items;
    this.itemHeight = itemHeight;
    this.visibleCount = Math.ceil(container.offsetHeight / itemHeight) + 2;
    this.startIndex = 0;
    
    this.init();
  }
  
  init() {
    // 创建滚动容器
    this.scrollContainer = document.createElement('div');
    this.scrollContainer.style.height = `${this.items.length * this.itemHeight}px`;
    this.scrollContainer.style.position = 'relative';
    
    // 创建可视区域
    this.visibleContainer = document.createElement('div');
    this.visibleContainer.style.position = 'absolute';
    this.visibleContainer.style.top = '0';
    this.visibleContainer.style.width = '100%';
    
    this.scrollContainer.appendChild(this.visibleContainer);
    this.container.appendChild(this.scrollContainer);
    
    // 监听滚动
    this.container.addEventListener('scroll', this.handleScroll.bind(this));
    
    this.render();
  }
  
  handleScroll() {
    const scrollTop = this.container.scrollTop;
    const newStartIndex = Math.floor(scrollTop / this.itemHeight);
    
    if (newStartIndex !== this.startIndex) {
      this.startIndex = newStartIndex;
      this.render();
    }
  }
  
  render() {
    const endIndex = Math.min(
      this.startIndex + this.visibleCount, 
      this.items.length
    );
    
    // 清空可视容器
    this.visibleContainer.innerHTML = '';
    
    // 渲染可见项
    for (let i = this.startIndex; i < endIndex; i++) {
      const item = document.createElement('div');
      item.style.position = 'absolute';
      item.style.top = `${i * this.itemHeight}px`;
      item.style.height = `${this.itemHeight}px`;
      item.style.width = '100%';
      item.textContent = this.items[i];
      
      this.visibleContainer.appendChild(item);
    }
  }
}

// 使用示例
const container = document.getElementById('scroll-container');
const items = Array.from({length: 10000}, (_, i) => `Item ${i + 1}`);
new VirtualScroller(container, items);

第三章:内存管理优化

3.1 识别和避免内存泄漏

// ❌ 常见的内存泄漏场景
class MemoryLeakExamples {
  constructor() {
    // 1. 未清理的定时器
    this.timer = setInterval(() => {
      console.log('Running...');
    }, 1000);
    
    // 2. 未移除的事件监听器
    this.handleClick = () => console.log('clicked');
    document.addEventListener('click', this.handleClick);
    
    // 3. 闭包引用
    this.data = new Array(1000000).fill('data');
    window.globalCallback = () => {
      return this.data; // 持续引用大对象
    };
  }
  
  // 忘记清理资源的析构函数
  destroy() {
    // 什么都没做,造成内存泄漏
  }
}

// ✅ 正确的内存管理
class ProperMemoryManagement {
  constructor() {
    this.data = new Array(1000000).fill('data');
    this.timers = [];
    this.eventListeners = [];
    
    // 记录定时器
    const timer = setInterval(() => {
      console.log('Running...');
    }, 1000);
    this.timers.push(timer);
    
    // 记录事件监听器
    this.handleClick = () => console.log('clicked');
    document.addEventListener('click', this.handleClick);
    this.eventListeners.push({
      element: document,
      event: 'click',
      handler: this.handleClick
    });
  }
  
  destroy() {
    // 清理定时器
    this.timers.forEach(timer => clearInterval(timer));
    this.timers = [];
    
    // 清理事件监听器
    this.eventListeners.forEach(({element, event, handler}) => {
      element.removeEventListener(event, handler);
    });
    this.eventListeners = [];
    
    // 清理引用
    this.data = null;
  }
}

3.2 对象池模式优化

// 对象池:复用对象,减少垃圾回收压力
class ObjectPool {
  constructor(createFn, resetFn, initialSize = 10) {
    this.createFn = createFn;
    this.resetFn = resetFn;
    this.pool = [];
    
    // 预创建对象
    for (let i = 0; i < initialSize; i++) {
      this.pool.push(this.createFn());
    }
  }
  
  acquire() {
    return this.pool.length > 0 
      ? this.pool.pop() 
      : this.createFn();
  }
  
  release(obj) {
    this.resetFn(obj);
    this.pool.push(obj);
  }
}

// 使用示例:DOM元素池
const domElementPool = new ObjectPool(
  () => {
    const div = document.createElement('div');
    div.className = 'pooled-element';
    return div;
  },
  (element) => {
    element.innerHTML = '';
    element.style.cssText = '';
    if (element.parentNode) {
      element.parentNode.removeChild(element);
    }
  },
  20
);

// 粒子系统示例
class Particle {
  constructor() {
    this.x = 0;
    this.y = 0;
    this.vx = 0;
    this.vy = 0;
    this.life = 0;
  }
  
  init(x, y, vx, vy, life) {
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
    this.life = life;
  }
  
  update() {
    this.x += this.vx;
    this.y += this.vy;
    this.life--;
  }
}

const particlePool = new ObjectPool(
  () => new Particle(),
  (particle) => particle.init(0, 0, 0, 0, 0)
);

第四章:算法和数据结构优化

4.1 选择合适的数据结构

// 不同场景下的最优数据结构选择

// ❌ 使用数组进行频繁的查找操作
class SlowLookup {
  constructor() {
    this.users = [];
  }
  
  addUser(user) {
    this.users.push(user);
  }
  
  findUser(id) {
    return this.users.find(user => user.id === id); // O(n)
  }
}

// ✅ 使用Map进行快速查找
class FastLookup {
  constructor() {
    this.users = new Map();
  }
  
  addUser(user) {
    this.users.set(user.id, user);
  }
  
  findUser(id) {
    return this.users.get(id); // O(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) => {
  console.log(`Computing for ${n}`);
  let result = 0;
  for (let i = 0; i < n * 1000000; i++) {
    result += i;
  }
  return result;
});

4.2 高效的数组操作

// 数组操作性能对比和优化

class ArrayOptimizations {
  static performance_test() {
    const size = 100000;
    const arr = Array.from({length: size}, (_, i) => i);
    
    // 1. for循环 vs forEach vs map
    console.time('for loop');
    let sum1 = 0;
    for (let i = 0; i < arr.length; i++) {
      sum1 += arr[i];
    }
    console.timeEnd('for loop');
    
    console.time('forEach');
    let sum2 = 0;
    arr.forEach(item => sum2 += item);
    console.timeEnd('forEach');
    
    console.time('reduce');
    const sum3 = arr.reduce((sum, item) => sum + item, 0);
    console.timeEnd('reduce');
  }
  
  // 高效的数组去重
  static uniqueArray(arr) {
    // 使用Set(最快)
    return [...new Set(arr)];
  }
  
  // 高效的数组扁平化
  static flattenArray(arr) {
    // 现代方法
    return arr.flat(Infinity);
    
    // 自定义实现(更好的控制)
    function flattenCustom(arr, depth = Infinity) {
      const result = [];
      for (const item of arr) {
        if (Array.isArray(item) && depth > 0) {
          result.push(...flattenCustom(item, depth - 1));
        } else {
          result.push(item);
        }
      }
      return result;
    }
  }
  
  // 大数组分批处理
  static async processBigArray(arr, processor, batchSize = 1000) {
    const results = [];
    
    for (let i = 0; i < arr.length; i += batchSize) {
      const batch = arr.slice(i, i + batchSize);
      const batchResults = await new Promise(resolve => {
        // 使用 setTimeout 避免阻塞主线程
        setTimeout(() => {
          resolve(batch.map(processor));
        }, 0);
      });
      results.push(...batchResults);
    }
    
    return results;
  }
}

第五章:异步优化

5.1 Promise优化技巧

// Promise性能优化实践

class AsyncOptimizations {
  // ❌ 串行处理(慢)
  static async slowAsyncProcess(urls) {
    const results = [];
    for (const url of urls) {
      const result = await fetch(url);
      results.push(await result.json());
    }
    return results;
  }
  
  // ✅ 并行处理(快)
  static async fastAsyncProcess(urls) {
    const promises = urls.map(url => 
      fetch(url).then(res => res.json())
    );
    return Promise.all(promises);
  }
  
  // 限制并发数的异步处理
  static async limitedConcurrency(urls, limit = 5) {
    const results = [];
    
    for (let i = 0; i < urls.length; i += limit) {
      const batch = urls.slice(i, i + limit);
      const batchPromises = batch.map(url => 
        fetch(url).then(res => res.json())
      );
      const batchResults = await Promise.all(batchPromises);
      results.push(...batchResults);
    }
    
    return results;
  }
  
  // 带超时的Promise
  static timeoutPromise(promise, timeout = 5000) {
    return Promise.race([
      promise,
      new Promise((_, reject) => 
        setTimeout(() => reject(new Error('Timeout')), timeout)
      )
    ]);
  }
  
  // Promise重试机制
  static async retryPromise(fn, maxRetries = 3, delay = 1000) {
    for (let i = 0; i < maxRetries; i++) {
      try {
        return await fn();
      } catch (error) {
        if (i === maxRetries - 1) throw error;
        await new Promise(resolve => setTimeout(resolve, delay * (i + 1)));
      }
    }
  }
}

5.2 Web Workers优化

// 主线程代码
class WorkerOptimization {
  constructor() {
    this.worker = new Worker('heavy-computation-worker.js');
    this.setupWorker();
  }
  
  setupWorker() {
    this.worker.onmessage = (event) => {
      const { taskId, result, error } = event.data;
      
      if (error) {
        console.error('Worker error:', error);
      } else {
        console.log('Task completed:', taskId, result);
      }
    };
  }
  
  async heavyComputation(data) {
    return new Promise((resolve, reject) => {
      const taskId = Date.now();
      
      const handler = (event) => {
        if (event.data.taskId === taskId) {
          this.worker.removeEventListener('message', handler);
          if (event.data.error) {
            reject(new Error(event.data.error));
          } else {
            resolve(event.data.result);
          }
        }
      };
      
      this.worker.addEventListener('message', handler);
      this.worker.postMessage({ taskId, data });
    });
  }
}
// heavy-computation-worker.js
self.onmessage = function(event) {
  const { taskId, data } = event.data;
  
  try {
    // 执行计算密集型任务
    const result = performHeavyCalculation(data);
    
    self.postMessage({
      taskId,
      result
    });
  } catch (error) {
    self.postMessage({
      taskId,
      error: error.message
    });
  }
};

function performHeavyCalculation(data) {
  // 模拟计算密集型任务
  let result = 0;
  for (let i = 0; i < data.iterations; i++) {
    result += Math.sqrt(i) * Math.sin(i);
  }
  return result;
}

第六章:代码分割和懒加载

6.1 动态导入优化

// 动态导入实现代码分割

class LazyLoader {
  constructor() {
    this.loadedModules = new Map();
  }
  
  // 懒加载模块
  async loadModule(moduleName) {
    if (this.loadedModules.has(moduleName)) {
      return this.loadedModules.get(moduleName);
    }
    
    try {
      const module = await import(`./modules/${moduleName}.js`);
      this.loadedModules.set(moduleName, module);
      return module;
    } catch (error) {
      console.error(`Failed to load module: ${moduleName}`, error);
      throw error;
    }
  }
  
  // 预加载重要模块
  async preloadModules(moduleNames) {
    const promises = moduleNames.map(name => 
      this.loadModule(name).catch(err => 
        console.warn(`Preload failed for ${name}:`, err)
      )
    );
    
    return Promise.allSettled(promises);
  }
  
  // 条件加载
  async loadIfNeeded(condition, moduleName) {
    if (condition) {
      return this.loadModule(moduleName);
    }
    return null;
  }
}

// 使用示例
const loader = new LazyLoader();

// 路由级别的懒加载
class Router {
  constructor() {
    this.routes = new Map();
    this.loader = new LazyLoader();
  }
  
  addRoute(path, moduleLoader) {
    this.routes.set(path, moduleLoader);
  }
  
  async navigate(path) {
    const moduleLoader = this.routes.get(path);
    if (!moduleLoader) {
      throw new Error(`Route not found: ${path}`);
    }
    
    // 显示加载状态
    this.showLoading();
    
    try {
      const module = await this.loader.loadModule(moduleLoader);
      await module.default.render();
    } catch (error) {
      console.error('Navigation failed:', error);
    } finally {
      this.hideLoading();
    }
  }
  
  showLoading() {
    document.getElementById('loading').style.display = 'block';
  }
  
  hideLoading() {
    document.getElementById('loading').style.display = 'none';
  }
}

6.2 图片懒加载优化

class LazyImageLoader {
  constructor(options = {}) {
    this.options = {
      rootMargin: '50px',
      threshold: 0.1,
      ...options
    };
    
    this.observer = new IntersectionObserver(
      this.handleIntersection.bind(this),
      this.options
    );
    
    this.loadedImages = new Set();
  }
  
  observe(images) {
    images.forEach(img => {
      if (!this.loadedImages.has(img)) {
        this.observer.observe(img);
      }
    });
  }
  
  handleIntersection(entries) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        this.loadImage(entry.target);
      }
    });
  }
  
  async loadImage(img) {
    const src = img.dataset.src;
    if (!src || this.loadedImages.has(img)) return;
    
    this.loadedImages.add(img);
    this.observer.unobserve(img);
    
    try {
      // 预加载图片
      await this.preloadImage(src);
      
      // 淡入效果
      img.style.opacity = '0';
      img.src = src;
      
      img.onload = () => {
        img.style.transition = 'opacity 0.3s';
        img.style.opacity = '1';
      };
      
    } catch (error) {
      console.error('Image load failed:', src, error);
      img.src = this.options.fallbackImage || '';
    }
  }
  
  preloadImage(src) {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => resolve(img);
      img.onerror = reject;
      img.src = src;
    });
  }
}

// 使用示例
const lazyLoader = new LazyImageLoader({
  rootMargin: '100px',
  fallbackImage: '/images/placeholder.png'
});

// 自动发现并懒加载所有图片
const lazyImages = document.querySelectorAll('img[data-src]');
lazyLoader.observe(lazyImages);

第七章:性能监控和分析

7.1 性能指标收集

class PerformanceAnalytics {
  constructor() {
    this.metrics = {};
    this.observer = null;
    this.init();
  }
  
  init() {
    // 监控核心性能指标
    this.collectCoreMetrics();
    
    // 监控长任务
    this.observeLongTasks();
    
    // 监控内存使用
    this.monitorMemoryUsage();
  }
  
  collectCoreMetrics() {
    // 页面加载性能
    window.addEventListener('load', () => {
      const navigation = performance.getEntriesByType('navigation')[0];
      
      this.metrics.loadTime = navigation.loadEventEnd - navigation.fetchStart;
      this.metrics.domContentLoaded = navigation.domContentLoadedEventEnd - navigation.fetchStart;
      this.metrics.firstPaint = this.getFirstPaint();
      this.metrics.firstContentfulPaint = this.getFirstContentfulPaint();
      
      this.reportMetrics();
    });
  }
  
  getFirstPaint() {
    const paintEntries = performance.getEntriesByType('paint');
    const firstPaint = paintEntries.find(entry => entry.name === 'first-paint');
    return firstPaint ? firstPaint.startTime : null;
  }
  
  getFirstContentfulPaint() {
    const paintEntries = performance.getEntriesByType('paint');
    const fcp = paintEntries.find(entry => entry.name === 'first-contentful-paint');
    return fcp ? fcp.startTime : null;
  }
  
  observeLongTasks() {
    if ('PerformanceObserver' in window) {
      this.observer = new PerformanceObserver((entryList) => {
        const entries = entryList.getEntries();
        entries.forEach(entry => {
          if (entry.duration > 50) {
            console.warn('Long task detected:', {
              duration: entry.duration,
              startTime: entry.startTime
            });
          }
        });
      });
      
      this.observer.observe({ entryTypes: ['longtask'] });
    }
  }
  
  monitorMemoryUsage() {
    if ('memory' in performance) {
      setInterval(() => {
        const memory = performance.memory;
        this.metrics.memoryUsage = {
          usedJSHeapSize: memory.usedJSHeapSize,
          totalJSHeapSize: memory.totalJSHeapSize,
          jsHeapSizeLimit: memory.jsHeapSizeLimit
        };
        
        // 警告内存使用过高
        const usagePercent = memory.usedJSHeapSize / memory.jsHeapSizeLimit;
        if (usagePercent > 0.8) {
          console.warn('High memory usage detected:', usagePercent);
        }
      }, 10000);
    }
  }
  
  // 自定义性能标记
  mark(name) {
    performance.mark(name);
  }
  
  measure(name, startMark, endMark) {
    performance.measure(name, startMark, endMark);
    const measures = performance.getEntriesByName(name, 'measure');
    return measures[measures.length - 1].duration;
  }
  
  reportMetrics() {
    // 发送性能数据到分析服务
    console.log('Performance Metrics:', this.metrics);
    
    // 这里可以发送到您的分析服务
    // this.sendToAnalytics(this.metrics);
  }
}

// 初始化性能监控
const analytics = new PerformanceAnalytics();

第八章:实战优化案例

8.1 列表渲染优化实战

// 大型列表优化完整解决方案
class OptimizedList {
  constructor(container, options = {}) {
    this.container = container;
    this.options = {
      itemHeight: 50,
      bufferSize: 5,
      renderBatchSize: 20,
      ...options
    };
    
    this.items = [];
    this.filteredItems = [];
    this.visibleItems = [];
    this.startIndex = 0;
    this.endIndex = 0;
    
    this.init();
  }
  
  init() {
    this.createStructure();
    this.bindEvents();
  }
  
  createStructure() {
    this.container.innerHTML = `
      <div class="list-search">
        <input type="text" placeholder="搜索..." />
      </div>
      <div class="list-viewport">
        <div class="list-spacer-before"></div>
        <div class="list-content"></div>
        <div class="list-spacer-after"></div>
      </div>
    `;
    
    this.searchInput = this.container.querySelector('.list-search input');
    this.viewport = this.container.querySelector('.list-viewport');
    this.spacerBefore = this.container.querySelector('.list-spacer-before');
    this.content = this.container.querySelector('.list-content');
    this.spacerAfter = this.container.querySelector('.list-spacer-after');
    
    // 设置样式
    this.viewport.style.cssText = `
      height: 400px;
      overflow-y: auto;
      position: relative;
    `;
  }
  
  bindEvents() {
    // 防抖搜索
    let searchTimeout;
    this.searchInput.addEventListener('input', (e) => {
      clearTimeout(searchTimeout);
      searchTimeout = setTimeout(() => {
        this.filterItems(e.target.value);
      }, 300);
    });
    
    // 滚动监听
    let scrollTimeout;
    this.viewport.addEventListener('scroll', () => {
      if (scrollTimeout) return;
      scrollTimeout = setTimeout(() => {
        this.handleScroll();
        scrollTimeout = null;
      }, 16); // 约60fps
    });
  }
  
  setItems(items) {
    this.items = items;
    this.filteredItems = [...items];
    this.render();
  }
  
  filterItems(searchTerm) {
    const term = searchTerm.toLowerCase();
    this.filteredItems = this.items.filter(item => 
      item.title.toLowerCase().includes(term) ||
      item.description.toLowerCase().includes(term)
    );
    this.render();
  }
  
  handleScroll() {
    const scrollTop = this.viewport.scrollTop;
    const containerHeight = this.viewport.offsetHeight;
    
    // 计算可见范围
    const startIndex = Math.floor(scrollTop / this.options.itemHeight);
    const visibleCount = Math.ceil(containerHeight / this.options.itemHeight);
    const endIndex = Math.min(
      startIndex + visibleCount + this.options.bufferSize * 2,
      this.filteredItems.length
    );
    
    const newStartIndex = Math.max(0, startIndex - this.options.bufferSize);
    
    if (newStartIndex !== this.startIndex || endIndex !== this.endIndex) {
      this.startIndex = newStartIndex;
      this.endIndex = endIndex;
      this.renderVisible();
    }
  }
  
  render() {
    this.startIndex = 0;
    this.endIndex = Math.min(
      this.options.renderBatchSize,
      this.filteredItems.length
    );
    this.renderVisible();
  }
  
  renderVisible() {
    // 更新占位空间
    const totalHeight = this.filteredItems.length * this.options.itemHeight;
    const beforeHeight = this.startIndex * this.options.itemHeight;
    const afterHeight = (this.filteredItems.length - this.endIndex) * this.options.itemHeight;
    
    this.spacerBefore.style.height = `${beforeHeight}px`;
    this.spacerAfter.style.height = `${afterHeight}px`;
    
    // 渲染可见项
    const fragment = document.createDocumentFragment();
    
    for (let i = this.startIndex; i < this.endIndex; i++) {
      const item = this.filteredItems[i];
      const itemElement = this.createItemElement(item, i);
      fragment.appendChild(itemElement);
    }
    
    this.content.innerHTML = '';
    this.content.appendChild(fragment);
  }
  
  createItemElement(item, index) {
    const element = document.createElement('div');
    element.className = 'list-item';
    element.style.cssText = `
      height: ${this.options.itemHeight}px;
      padding: 10px;
      border-bottom: 1px solid #eee;
      display: flex;
      align-items: center;
    `;
    
    element.innerHTML = `
      <div class="item-content">
        <h3>${item.title}</h3>
        <p>${item.description}</p>
      </div>
    `;
    
    return element;
  }
}

// 使用示例
const listContainer = document.getElementById('optimized-list');
const optimizedList = new OptimizedList(listContainer);

// 生成大量测试数据
const testItems = Array.from({length: 10000}, (_, i) => ({
  id: i,
  title: `Item ${i + 1}`,
  description: `Description for item ${i + 1} with some additional text`
}));

optimizedList.setItems(testItems);

8.2 表单验证优化

// 高性能表单验证系统
class OptimizedFormValidator {
  constructor(form) {
    this.form = form;
    this.rules = new Map();
    this.errors = new Map();
    this.validateQueue = [];
    this.isValidating = false;
    
    this.init();
  }
  
  init() {
    this.bindEvents();
    this.createErrorContainer();
  }
  
  bindEvents() {
    // 使用事件委托减少监听器数量
    this.form.addEventListener('input', this.debounce(
      (e) => this.handleInput(e), 300
    ));
    
    this.form.addEventListener('blur', (e) => {
      if (e.target.matches('input, textarea, select')) {
        this.validateField(e.target);
      }
    }, true);
    
    this.form.addEventListener('submit', (e) => {
      e.preventDefault();
      this.validateAll();
    });
  }
  
  debounce(func, wait) {
    const timeouts = new Map();
    return function(e) {
      const key = e.target;
      clearTimeout(timeouts.get(key));
      timeouts.set(key, setTimeout(() => {
        func.call(this, e);
        timeouts.delete(key);
      }, wait));
    }.bind(this);
  }
  
  addRule(fieldName, validators) {
    this.rules.set(fieldName, validators);
  }
  
  handleInput(e) {
    const field = e.target;
    if (this.rules.has(field.name)) {
      this.queueValidation(field);
    }
  }
  
  queueValidation(field) {
    // 防止重复排队
    if (!this.validateQueue.includes(field)) {
      this.validateQueue.push(field);
    }
    
    if (!this.isValidating) {
      this.processValidationQueue();
    }
  }
  
  async processValidationQueue() {
    this.isValidating = true;
    
    while (this.validateQueue.length > 0) {
      const field = this.validateQueue.shift();
      await this.validateField(field);
      
      // 让出主线程,避免阻塞UI
      await new Promise(resolve => setTimeout(resolve, 0));
    }
    
    this.isValidating = false;
  }
  
  async validateField(field) {
    const validators = this.rules.get(field.name);
    if (!validators) return true;
    
    const errors = [];
    
    for (const validator of validators) {
      try {
        const isValid = await validator.validate(field.value, field);
        if (!isValid) {
          errors.push(validator.message);
        }
      } catch (error) {
        errors.push('验证过程中发生错误');
      }
    }
    
    if (errors.length > 0) {
      this.errors.set(field.name, errors);
      this.showFieldError(field, errors);
      return false;
    } else {
      this.errors.delete(field.name);
      this.hideFieldError(field);
      return true;
    }
  }
  
  async validateAll() {
    const fields = this.form.querySelectorAll('input, textarea, select');
    const validationPromises = Array.from(fields)
      .filter(field => this.rules.has(field.name))
      .map(field => this.validateField(field));
    
    const results = await Promise.all(validationPromises);
    const isValid = results.every(result => result);
    
    if (isValid) {
      this.onValidationSuccess();
    } else {
      this.onValidationError();
    }
  }
  
  showFieldError(field, errors) {
    const errorContainer = field.parentNode.querySelector('.field-error');
    if (errorContainer) {
      errorContainer.textContent = errors[0];
      errorContainer.style.display = 'block';
    }
    field.classList.add('error');
  }
  
  hideFieldError(field) {
    const errorContainer = field.parentNode.querySelector('.field-error');
    if (errorContainer) {
      errorContainer.style.display = 'none';
    }
    field.classList.remove('error');
  }
  
  createErrorContainer() {
    const fields = this.form.querySelectorAll('input, textarea, select');
    fields.forEach(field => {
      if (!field.parentNode.querySelector('.field-error')) {
        const errorDiv = document.createElement('div');
        errorDiv.className = 'field-error';
        errorDiv.style.cssText = `
          color: #dc3545;
          font-size: 14px;
          margin-top: 5px;
          display: none;
        `;
        field.parentNode.appendChild(errorDiv);
      }
    });
  }
  
  onValidationSuccess() {
    console.log('表单验证通过!');
    // 这里可以提交表单数据
  }
  
  onValidationError() {
    console.log('表单验证失败:', Object.fromEntries(this.errors));
  }
}

// 验证器工厂
class ValidatorFactory {
  static required(message = '此字段为必填项') {
    return {
      message,
      validate: (value) => value.trim().length > 0
    };
  }
  
  static email(message = '请输入有效的邮箱地址') {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return {
      message,
      validate: (value) => !value || emailRegex.test(value)
    };
  }
  
  static async remote(url, message = '验证失败') {
    return {
      message,
      validate: async (value) => {
        if (!value) return true;
        
        try {
          const response = await fetch(url, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ value })
          });
          const result = await response.json();
          return result.valid;
        } catch (error) {
          console.error('Remote validation error:', error);
          return false;
        }
      }
    };
  }
  
  static length(min, max, message) {
    return {
      message: message || `长度应在${min}-${max}个字符之间`,
      validate: (value) => {
        const len = value.length;
        return len >= min && len <= max;
      }
    };
  }
}

// 使用示例
const form = document.getElementById('user-form');
const validator = new OptimizedFormValidator(form);

// 配置验证规则
validator.addRule('email', [
  ValidatorFactory.required(),
  ValidatorFactory.email(),
  await ValidatorFactory.remote('/api/check-email', '邮箱已被占用')
]);

validator.addRule('password', [
  ValidatorFactory.required(),
  ValidatorFactory.length(8, 20)
]);

第九章:构建和部署优化

9.1 代码压缩和Tree Shaking

// webpack.config.js 优化配置示例
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  
  output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js',
    path: path.resolve(__dirname, 'dist'),
    clean: true
  },
  
  optimization: {
    // Tree Shaking
    usedExports: true,
    sideEffects: false,
    
    // 代码分割
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
          priority: 10
        },
        common: {
          minChunks: 2,
          chunks: 'all',
          name: 'common',
          priority: 5
        }
      }
    },
    
    // 代码压缩
    minimizer: [
      new TerserPlugin({
        parallel: true,
        terserOptions: {
          compress: {
            drop_console: true, // 移除console
            drop_debugger: true,
            pure_funcs: ['console.log'] // 移除指定函数调用
          },
          mangle: true,
          format: {
            comments: false
          }
        }
      })
    ]
  },
  
  plugins: [
    // 分析打包大小
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      openAnalyzer: false
    })
  ]
};

9.2 CDN和缓存策略

// 智能资源加载器
class ResourceLoader {
  constructor(options = {}) {
    this.options = {
      cdnBase: 'https://cdn.example.com',
      fallbackBase: '/static',
      cacheVersion: '1.0.0',
      ...options
    };
    
    this.loadedResources = new Set();
    this.failedCDN = new Set();
  }
  
  async loadScript(src, options = {}) {
    const cacheKey = `${src}_${this.options.cacheVersion}`;
    
    if (this.loadedResources.has(cacheKey)) {
      return true;
    }
    
    // 尝试CDN加载
    const cdnUrl = `${this.options.cdnBase}${src}`;
    const fallbackUrl = `${this.options.fallbackBase}${src}`;
    
    try {
      if (!this.failedCDN.has(src)) {
        await this.loadScriptFromUrl(cdnUrl, options);
        this.loadedResources.add(cacheKey);
        return true;
      }
    } catch (error) {
      console.warn('CDN load failed, trying fallback:', error);
      this.failedCDN.add(src);
    }
    
    // 降级到本地资源
    try {
      await this.loadScriptFromUrl(fallbackUrl, options);
      this.loadedResources.add(cacheKey);
      return true;
    } catch (error) {
      console.error('Resource load failed:', src, error);
      return false;
    }
  }
  
  loadScriptFromUrl(url, options = {}) {
    return new Promise((resolve, reject) => {
      const script = document.createElement('script');
      
      script.async = options.async !== false;
      script.defer = options.defer || false;
      script.crossOrigin = options.crossOrigin || 'anonymous';
      
      script.onload = () => resolve(script);
      script.onerror = () => reject(new Error(`Failed to load: ${url}`));
      
      script.src = url;
      document.head.appendChild(script);
    });
  }
  
  // 预加载关键资源
  preloadResources(resources) {
    const promises = resources.map(resource => {
      const link = document.createElement('link');
      link.rel = 'preload';
      link.href = resource.url;
      link.as = resource.type;
      document.head.appendChild(link);
      
      return new Promise(resolve => {
        link.onload = resolve;
        link.onerror = resolve; // 预加载失败不影响主流程
      });
    });
    
    return Promise.all(promises);
  }
}

第十章:性能优化清单

10.1 开发阶段检查清单

// 性能优化自检工具
class PerformanceChecker {
  static runChecks() {
    const results = {
      passed: [],
      failed: [],
      warnings: []
    };
    
    // 检查全局变量
    this.checkGlobalVariables(results);
    
    // 检查内存泄漏
    this.checkMemoryLeaks(results);
    
    // 检查DOM操作
    this.checkDOMOperations(results);
    
    // 检查事件监听器
    this.checkEventListeners(results);
    
    return results;
  }
  
  static checkGlobalVariables(results) {
    const globalVars = Object.keys(window).filter(key => 
      !['console', 'document', 'window', 'navigator'].includes(key) &&
      typeof window[key] !== 'function'
    );
    
    if (globalVars.length > 10) {
      results.warnings.push(`发现${globalVars.length}个全局变量,建议减少`);
    } else {
      results.passed.push('全局变量数量合理');
    }
  }
  
  static checkMemoryLeaks(results) {
    if (performance.memory) {
      const memoryUsage = performance.memory.usedJSHeapSize / performance.memory.jsHeapSizeLimit;
      
      if (memoryUsage > 0.8) {
        results.failed.push('内存使用率过高,可能存在内存泄漏');
      } else if (memoryUsage > 0.6) {
        results.warnings.push('内存使用率较高,需要关注');
      } else {
        results.passed.push('内存使用正常');
      }
    }
  }
  
  static checkDOMOperations(results) {
    // 检查是否存在频繁的DOM查询
    const originalQuerySelector = document.querySelector;
    let querySelectorCalls = 0;
    
    document.querySelector = function(...args) {
      querySelectorCalls++;
      return originalQuerySelector.apply(this, args);
    };
    
    setTimeout(() => {
      if (querySelectorCalls > 100) {
        results.warnings.push(`DOM查询次数过多(${querySelectorCalls}),建议缓存选择器结果`);
      } else {
        results.passed.push('DOM查询次数合理');
      }
      
      // 恢复原始方法
      document.querySelector = originalQuerySelector;
    }, 1000);
  }
  
  static checkEventListeners(results) {
    const eventListenerCount = getEventListeners ? 
      Object.keys(getEventListeners(document)).length : 0;
    
    if (eventListenerCount > 50) {
      results.warnings.push(`事件监听器过多(${eventListenerCount}),建议使用事件委托`);
    } else {
      results.passed.push('事件监听器数量合理');
    }
  }
}

// 性能优化建议生成器
class PerformanceAdvisor {
  static generateReport() {
    const report = {
      timestamp: new Date().toISOString(),
      metrics: this.collectMetrics(),
      suggestions: this.generateSuggestions()
    };
    
    return report;
  }
  
  static collectMetrics() {
    const navigation = performance.getEntriesByType('navigation')[0];
    
    return {
      loadTime: navigation ? navigation.loadEventEnd - navigation.fetchStart : 0,
      domContentLoaded: navigation ? navigation.domContentLoadedEventEnd - navigation.fetchStart : 0,
      resourceCount: performance.getEntriesByType('resource').length,
      memoryUsage: performance.memory ? performance.memory.usedJSHeapSize : 0
    };
  }
  
  static generateSuggestions() {
    const suggestions = [];
    const metrics = this.collectMetrics();
    
    if (metrics.loadTime > 3000) {
      suggestions.push({
        type: 'critical',
        message: '页面加载时间过长,建议:代码分割、图片优化、启用缓存'
      });
    }
    
    if (metrics.resourceCount > 50) {
      suggestions.push({
        type: 'warning',
        message: '资源请求过多,建议:合并文件、使用雪碧图、启用HTTP/2'
      });
    }
    
    if (metrics.memoryUsage > 50 * 1024 * 1024) { // 50MB
      suggestions.push({
        type: 'warning',
        message: '内存使用较高,建议:检查内存泄漏、优化数据结构'
      });
    }
    
    return suggestions;
  }
}

10.2 最佳实践总结

// JavaScript性能优化最佳实践汇总
const PerformanceBestPractices = {
  // 变量和作用域
  variables: {
    // ✅ 使用局部变量
    useLocalVariables() {
      function goodExample() {
        const localVar = document.getElementById('element');
        localVar.style.color = 'red';
        localVar.textContent = 'Updated';
      }
    },
    
    // ✅ 缓存重复计算
    cacheComputations() {
      const expensiveResult = memoize((input) => {
        // 昂贵的计算
        return input * input * input;
      });
    }
  },
  
  // 循环优化
  loops: {
    // ✅ 缓存数组长度
    cacheArrayLength() {
      const items = getItems();
      for (let i = 0, len = items.length; i < len; i++) {
        processItem(items[i]);
      }
    },
    
    // ✅ 使用合适的循环方式
    chooseRightLoop() {
      // for循环 - 最快,适合简单遍历
      // forEach - 可读性好,适合函数式编程
      // map/filter/reduce - 适合数据转换
      // for...of - 适合迭代器对象
    }
  },
  
  // 异步操作
  async: {
    // ✅ 并行处理
    parallelProcessing: async () => {
      const results = await Promise.all([
        fetch('/api/data1'),
        fetch('/api/data2'),
        fetch('/api/data3')
      ]);
    },
    
    // ✅ 使用Web Workers
    useWebWorkers() {
      const worker = new Worker('heavy-computation.js');
      worker.postMessage(data);
      return new Promise(resolve => {
        worker.onmessage = (e) => resolve(e.data);
      });
    }
  },
  
  // DOM操作
  dom: {
    // ✅ 批量操作
    batchOperations() {
      const fragment = document.createDocumentFragment();
      for (let i = 0; i < 100; i++) {
        const element = document.createElement('div');
        fragment.appendChild(element);
      }
      document.body.appendChild(fragment);
    },
    
    // ✅ 使用事件委托
    eventDelegation() {
      document.body.addEventListener('click', (e) => {
        if (e.target.classList.contains('button')) {
          handleButtonClick(e.target);
        }
      });
    }
  }
};

总结

JavaScript性能优化是一个系统工程,需要从多个维度来考虑:

🎯 核心要点回顾

  1. 性能监控先行 - 先测量,再优化,避免盲目优化
  2. DOM操作优化 - 批量操作、缓存选择器、虚拟滚动
  3. 内存管理 - 避免内存泄漏、合理使用对象池
  4. 算法优化 - 选择合适的数据结构和算法
  5. 异步处理 - 并行执行、Web Workers、合理使用Promise
  6. 代码分割 - 按需加载、懒加载、Tree Shaking
  7. 构建优化 - 代码压缩、缓存策略、CDN部署

💡 优化原则

  • 80/20原则 - 20%的代码消耗80%的性能,找到瓶颈点重点优化
  • 用户体验优先 - 优化应该以提升用户体验为目标
  • 渐进式优化 - 从影响最大的地方开始,逐步优化
  • 持续监控 - 建立性能监控体系,持续跟踪优化效果

🚀 进阶学习方向

  1. 框架特定优化 - React、Vue、Angular的性能优化策略
  2. 服务端优化 - SSR、缓存策略、CDN配置
  3. 移动端优化 - 触摸事件、电池优化、网络适配
  4. PWA优化 - Service Worker、离线缓存、推送通知

记住,性能优化没有银弹,需要根据具体场景选择合适的优化策略。关键是要建立性能意识,在开发过程中时刻考虑性能问题,这样才能写出高质量的JavaScript代码!


“过早的优化是万恶之源,但合适的优化是成功之本”

Logo

为武汉地区的开发者提供学习、交流和合作的平台。社区聚集了众多技术爱好者和专业人士,涵盖了多个领域,包括人工智能、大数据、云计算、区块链等。社区定期举办技术分享、培训和活动,为开发者提供更多的学习和交流机会。

更多推荐