JavaScript 设计模式:从理论到实践

设计模式是解决常见问题的成熟方案。本文介绍 JavaScript 中最常用的几种设计模式,以及如何在实际项目中应用它们

2024-06-01

单例模式

确保一个类只有一个实例,并提供一个全局访问点。

class Singleton {
  static #instance = null;

  constructor() {
    if (Singleton.#instance) {
      return Singleton.#instance;
    }
    Singleton.#instance = this;
  }

  static getInstance() {
    if (!Singleton.#instance) {
      Singleton.#instance = new Singleton();
    }
    return Singleton.#instance;
  }
}

// 使用
const a = Singleton.getInstance();
const b = Singleton.getInstance();
console.log(a === b); // true

工厂模式

根据参数创建不同类型的对象,隐藏创建逻辑。

class ButtonFactory {
  create(type) {
    switch(type) {
      case 'primary':
        return new PrimaryButton();
      case 'secondary':
        return new SecondaryButton();
      case 'danger':
        return new DangerButton();
      default:
        throw new Error('Unknown button type');
    }
  }
}

观察者模式

定义对象间的一对多依赖,当一个对象改变时,所有依赖者都会收到通知。

class EventEmitter {
  #listeners = new Map();

  on(event, callback) {
    if (!this.#listeners.has(event)) {
      this.#listeners.set(event, []);
    }
    this.#listeners.get(event).push(callback);
  }

  emit(event, data) {
    const callbacks = this.#listeners.get(event) || [];
    callbacks.forEach(cb => cb(data));
  }

  off(event, callback) {
    const callbacks = this.#listeners.get(event) || [];
    const index = callbacks.indexOf(callback);
    if (index > -1) {
      callbacks.splice(index, 1);
    }
  }
}

// 使用
const emitter = new EventEmitter();
emitter.on('data', (value) => console.log(value));
emitter.emit('data', 'Hello World');

策略模式

定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。

const strategies = {
  creditCard: {
    pay(amount) {
      console.log(`Paid ${amount} using Credit Card`);
    }
  },
  paypal: {
    pay(amount) {
      console.log(`Paid ${amount} using PayPal`);
    }
  },
  crypto: {
    pay(amount) {
      console.log(`Paid ${amount} using Crypto`);
    }
  }
};

class PaymentContext {
  setStrategy(strategy) {
    this.strategy = strategy;
  }

  pay(amount) {
    this.strategy.pay(amount);
  }
}

// 使用
const payment = new PaymentContext();
payment.setStrategy(strategies.paypal);
payment.pay(100);

装饰器模式

动态地给对象添加额外的职责。

function withLogging(fn) {
  return function(...args) {
    console.log(`Calling ${fn.name} with`, args);
    const result = fn.apply(this, args);
    console.log(`Result:`, result);
    return result;
  };
}

function add(a, b) {
  return a + b;
}

const loggedAdd = withLogging(add);
loggedAdd(2, 3); // 输出调用日志

总结

设计模式不是银弹,但它们是经过验证的解决方案。重要的是理解每种模式背后的思想,而不是生搬硬套。在实际项目中,通常会根据需要组合多种模式,或者对经典模式进行变体。

"设计模式的出现是为了复用成功的解决方案,让代码更加灵活、可维护。" — GoF