设计模式
常见创建型、结构型、行为型模式在 JavaScript 中的实现示例。对象创建与继承见 原型与面向对象。
一、创建型模式
创建型模式关注对象如何被创建,隐藏创建逻辑,提升代码灵活性和复用性。
1. 单例模式(Singleton)
定义:确保一个类只有一个实例,并提供一个全局访问点。
实现:利用闭包或 ES6 模块(模块本身就是单例)。
// 方式1:闭包
const Singleton = (function () {
let instance;
function init() {
return { name: "I am the only one" };
}
return {
getInstance() {
if (!instance) instance = init();
return instance;
},
};
})();
// 方式2:ES6 模块(推荐)
// singleton.js
let instance = null;
export default {
getInstance() {
if (!instance) instance = { data: "shared" };
return instance;
},
};
应用场景:全局状态管理(Vuex / Redux store)、日志记录器、线程池。
2. 工厂模式(Factory)
定义:定义一个创建对象的接口,由子类或具体工厂类决定实例化哪个类。简单工厂、工厂方法和抽象工厂。
简单工厂示例:
class Button {
render() {
console.log("render button");
}
}
class Input {
render() {
console.log("render input");
}
}
class ComponentFactory {
static create(type) {
switch (type) {
case "button":
return new Button();
case "input":
return new Input();
default:
throw new Error("Unknown type");
}
}
}
const btn = ComponentFactory.create("button");
应用场景:根据配置动态创建 UI 组件、数据模型解析。
3. 构造函数 + 原型模式(Constructor & Prototype)
在 JavaScript 中,通过构造函数定义实例属性,通过原型定义共享方法,实现“类”的效果。这是 JS 原生的对象创建模式。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function () {
console.log(`Hello, I'm ${this.name}`);
};
const p1 = new Person("Alice", 25);
ES6 class 本质就是构造函数的语法糖,结合原型链。
应用场景:任何需要创建多个相似对象的场景,是 JS 面向对象的基础。
二、结构型模式
结构型模式关注如何组合类和对象以形成更大的结构。
1. 适配器模式(Adapter)
定义:将一个类的接口转换成客户端期望的另一个接口,使原本不兼容的类可以一起工作。
// 旧接口
class OldAPI {
request(data) {
return `Old: ${data}`;
}
}
// 适配器:将旧接口适配成新接口
class Adapter {
constructor(oldApi) {
this.oldApi = oldApi;
}
newRequest(data) {
const result = this.oldApi.request(data);
return `Adapted: ${result}`;
}
}
// 使用
const old = new OldAPI();
const adapted = new Adapter(old);
console.log(adapted.newRequest("hello")); // Adapted: Old: hello
应用场景:封装浏览器 API 差异(如 XMLHttpRequest 与 fetch)、第三方库接口统一。
2. 装饰器模式(Decorator)
定义:动态地给对象添加额外职责,比继承更灵活。在 React 中高阶组件(HOC)就是装饰器思想。
// 基础函数
function log(message) {
console.log(message);
}
// 装饰器:增加时间戳
function withTimestamp(fn) {
return function (...args) {
const timestamp = new Date().toISOString();
fn(`[${timestamp}] ${args[0]}`);
};
}
const decoratedLog = withTimestamp(log);
decoratedLog("Hello"); // 输出带时间戳的日志
React HOC 示例:
function withAuth(Component) {
return function Authenticated(props) {
if (!props.isLoggedIn) return <LoginPage />;
return <Component {...props} />;
};
}
应用场景:日志增强、权限控制、性能监控。
3. 代理模式(Proxy)
定义:为其他对象提供一种代理以控制对这个对象的访问。JavaScript 的 Proxy 对象直接实现了该模式。
const target = { name: "Alice", age: 25 };
const handler = {
get(obj, prop) {
if (prop === "age") return obj[prop] + " years old";
return obj[prop];
},
set(obj, prop, value) {
if (prop === "age" && value < 0) throw new Error("Age cannot be negative");
obj[prop] = value;
return true;
},
};
const proxy = new Proxy(target, handler);
console.log(proxy.age); // "25 years old"
proxy.age = -1; // Error
应用场景:Vue 3 响应式系统、数据校验、访问控制、缓存代理。
4. 发布-订阅模式(Publish-Subscribe)与事件总线
定义:发布者发布事件,订阅者监听事件,彼此解耦。不同于观察者模式(下面介绍),发布-订阅通常有一个事件中心作为调度器。
class EventBus {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback);
}
emit(event, data) {
(this.events[event] || []).forEach(cb => cb(data));
}
off(event, callback) {
if (!callback) delete this.events[event];
else this.events[event] = this.events[event]?.filter(cb => cb !== callback);
}
}
const bus = new EventBus();
bus.on("userLogin", user => console.log(`${user} logged in`));
bus.emit("userLogin", "Alice");
应用场景:跨组件通信(Vue 的 EventBus)、微前端通信、全局消息通知。
三、行为型模式
行为型模式关注对象之间的通信和职责分配。
1. 观察者模式(Observer)
定义:定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知。它与发布-订阅的区别:观察者模式中观察者直接注册在被观察者上,没有中央调度器。
使用 EventTarget 实现:
class Subject extends EventTarget {
setState(value) {
this.state = value;
this.dispatchEvent(new CustomEvent("change", { detail: value }));
}
}
const sub = new Subject();
sub.addEventListener("change", e => console.log("Observer 1:", e.detail));
sub.addEventListener("change", e => console.log("Observer 2:", e.detail));
sub.setState(42);
应用场景:DOM 事件监听、响应式数据绑定(如 MobX)。
2. 策略模式(Strategy)
定义:定义一系列算法,把它们封装起来,并使它们可以相互替换。算法的变化独立于使用它的客户端。
// 策略集
const strategies = {
plus: (a, b) => a + b,
minus: (a, b) => a - b,
multiply: (a, b) => a * b,
};
function execute(operation, a, b) {
return strategies[operation](a, b);
}
console.log(execute("plus", 5, 3)); // 8
应用场景:表单验证规则、动画缓动算法、路由匹配策略。
3. 迭代器模式(Iterator)
定义:提供一种方法顺序访问聚合对象中的各个元素,而不暴露其内部表示。JavaScript 内置了迭代器协议(Symbol.iterator)。
// 自定义可迭代对象
const myCollection = {
items: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => ({
value: this.items[index],
done: index++ >= this.items.length,
}),
};
},
};
for (const val of myCollection) console.log(val); // 1,2,3
应用场景:遍历自定义数据结构、实现 for...of 兼容性、分页数据迭代器。
4. 命令模式(Command)
定义:将请求封装成对象,从而使您可以用不同的请求对客户进行参数化、排队或记录日志,以及支持可撤销操作。
class Command {
constructor(receiver, action) {
this.receiver = receiver;
this.action = action;
}
execute() {
this.receiver[this.action]();
}
}
class TV {
on() {
console.log("TV on");
}
off() {
console.log("TV off");
}
}
const tv = new TV();
const onCommand = new Command(tv, "on");
const offCommand = new Command(tv, "off");
onCommand.execute(); // TV on
应用场景:撤销/重做(如编辑器)、事务操作、宏命令。
5. 职责链模式(Chain of Responsibility)
定义:使多个对象都有机会处理请求,将这些对象连成一条链,并沿着链传递请求,直到有一个对象处理它为止。
class Handler {
setNext(handler) {
this.next = handler;
return handler;
}
handle(request) {
if (this.canHandle(request)) return this.process(request);
if (this.next) return this.next.handle(request);
return "No handler found";
}
}
class AuthHandler extends Handler {
canHandle(req) {
return req.needAuth;
}
process(req) {
return "Authenticated";
}
}
class LoggerHandler extends Handler {
canHandle(req) {
return true;
}
process(req) {
console.log("Log request");
return this.next?.handle(req);
}
}
// 使用
const auth = new AuthHandler();
const logger = new LoggerHandler();
auth.setNext(logger);
auth.handle({ needAuth: true });
应用场景:中间件(Express/Koa 中间件链条)、表单校验预处理、权限校验升级链。
总结对比
| 分类 | 模式 | 核心特点 | 前端典型应用 |
|---|---|---|---|
| 创建型 | 单例 | 全局唯一实例 | Store、全局弹窗管理器 |
| 工厂 | 封装对象创建逻辑 | 动态组件创建、API 服务生成 | |
| 构造函数+原型 | JS 原生对象创建模式 | 所有基于 class/构造函数的对象 | |
| 结构型 | 适配器 | 接口转换,兼容旧代码 | 封装不同浏览器 API、第三方库适配 |
| 装饰器(HOC) | 动态增强功能 | React HOC、日志/性能装饰器 | |
| 代理(Proxy) | 控制访问,Vue 响应式核心 | 数据劫持、图片懒加载代理 | |
| 发布-订阅(事件总线) | 通过事件中心解耦发布者和订阅者 | 跨组件通信、全局事件通知 | |
| 行为型 | 观察者(EventTarget) | 目标对象直接通知观察者 | DOM 事件、响应式库的内部机制 |
| 策略 | 算法可互换 | 表单验证策略、动画算法 | |
| 迭代器 | 统一遍历接口 | 自定义 for...of 数据结构 | |
| 命令 | 封装请求为对象 | 撤销重做、操作日志 | |
| 职责链 | 请求沿链传递直到被处理 | Express 中间件、表单校验链 |
理解这些设计模式有助于阅读 Redux、Vue、Express 等库的架构;新代码应优先简单直接的模块划分,避免过度设计。
参考文献
以下链接在编写时均可正常访问:
| 资料 | 说明 |
|---|---|
| MDN:模块模式 | 模块与封装 |
| Refactoring Guru:设计模式 | 模式目录(中文) |
| MDN:Proxy | 代理模式实现基础 |
相关文章
错误处理与调试
结构化错误类型、try/catch 与全局未捕获处理是健壮应用的基础。
浏览器相关 API
DOM 事件、存储、fetch、Worker、Observer 等由浏览器提供的 Web API(非 ECMAScript 核心)。HTML 结构见 标签。
性能优化
防抖/节流、渲染与内存、资源预加载及引擎行为是前端性能的主要抓手。相关 CSS 见 视觉效果、Head 资源提示。
模块化与工程化
ESM、CommonJS 与打包工具(Tree Shaking、代码分割)是前端工程化的基础。入门见 JavaScript 基础 · 模块。
执行模型与作用域
执行上下文与词法作用域决定变量在何处可访问、闭包如何形成,以及 var / let / const 的差异。语言概览见 JavaScript 基础。
数据结构与集合增强
Map / Set、WeakMap / WeakSet、数组高阶方法、TypedArray 与 Symbol 等扩展了内置数据结构能力。见 语言基础。