010-JavaScript设计模式+手撕

JavaScript 设计模式常见类型

1. 单例模式(Singleton):确保一个类只有一个实例,并提供全局访问点。

2. 工厂模式(Factory):定义一个创建对象的接口,由子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

3. 抽象工厂模式(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

4. 建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

5. 原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

6. 适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

7. 装饰者模式(Decorator):动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

8. 代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。

9. 观察者模式(Observer):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

10. 发布订阅模式(Pub-Sub):与观察者模式类似,但有一个中间的事件通道,发布者和订阅者不直接通信,而是通过这个通道来传递消息。

11. 策略模式(Strategy):定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。

12. 命令模式(Command):将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

13. 状态模式(State):允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

14. 模板方法模式(Template Method):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

15. 迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

16. 组合模式(Composite):将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。


1. 创建型模式

  • **单例模式 (Singleton)**
    确保一个类只有一个实例(如全局状态管理)。
  • **工厂模式 (Factory)**
    封装对象的创建过程(如 createElement())。
  • **建造者模式 (Builder)**
    分步骤构建复杂对象(如配置对象生成)。

2. 结构型模式

  • **适配器模式 (Adapter)**
    兼容不同接口(如数据格式转换)。
  • **装饰器模式 (Decorator)**
    动态扩展对象功能(ES7/TypeScript 装饰器)。
  • **代理模式 (Proxy)**
    控制对象访问(如 ES6 Proxy 对象)。

3. 行为型模式

  • **观察者模式 (Observer)**
    直接通知依赖对象(如 MutationObserver)。
  • **发布订阅模式 (Pub/Sub)**
    通过事件中心通信(如 EventEmitter)。
  • **策略模式 (Strategy)**
    封装可互换的算法(如验证规则集)。
  • **迭代器模式 (Iterator)**
    提供遍历集合的方法(如 ES6 Iterator)。

观察者 (Observer) vs 发布订阅 (Pub/Sub) 的区别

特性 观察者模式 发布订阅模式
通信方式 直接通信 通过事件中心间接通信
耦合关系 观察者与目标相互知晓(较高耦合) 发布/订阅者彼此独立(完全解耦)
灵活性 依赖关系固定 动态订阅/取消,支持多对多关系
典型场景 对象状态变化直接通知 跨模块通信、事件驱动架构
实现复杂度 较简单 需维护事件中心(略复杂)

核心区别图解

观察者模式:   Subject →→ 直接通知 →→ Observer
发布订阅模式: Publisher → 事件中心 → Subscriber

手写发布订阅模式实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class EventEmitter {
constructor() {
this.events = {}; // 存储事件回调 { eventName: [callback1, callback2] }
}

// 订阅事件
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(callback);
}

// 发布事件
emit(eventName, ...args) {
const callbacks = this.events[eventName];
if (callbacks) {
callbacks.forEach(cb => cb(...args)); // 触发所有回调
}
}

// 取消订阅
off(eventName, callback) {
const callbacks = this.events[eventName];
if (callbacks) {
this.events[eventName] = callbacks.filter(cb => cb !== callback);
}
}

// 单次订阅
once(eventName, callback) {
const onceWrapper = (...args) => {
callback(...args);
this.off(eventName, onceWrapper); // 执行后立即取消
};
this.on(eventName, onceWrapper);
}
}

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const bus = new EventEmitter();

// 订阅事件
bus.on("message", (data) => {
console.log(`收到消息: ${data}`);
});

// 单次订阅
bus.once("alert", (text) => {
console.log(`一次性提示: ${text}`);
});

// 发布事件
bus.emit("message", "Hello World"); // 输出: 收到消息: Hello World
bus.emit("alert", "仅出现一次"); // 输出: 一次性提示: 仅出现一次
bus.emit("alert", "再次触发"); // 无输出(已取消)

// 取消订阅
const handler = (data) => console.log(`日志: ${data}`);
bus.on("log", handler);
bus.off("log", handler);
bus.emit("log", "测试"); // 无输出

关键特性

  • 解耦:发布/订阅者无需知道对方存在
  • 多对多:一个事件可被多次订阅,一次发布触发所有回调
  • 动态管理:支持运行时订阅/取消

实际应用场景:跨组件通信、插件系统、异步任务调度等。