一、创建型模式
1.1 单例模式(Singleton)
确保一个类只有一个实例,并提供一个全局访问点。
javascript
// 1. 使用闭包实现单例
const Singleton = (function () {
let instance = null;
function createInstance() {
return {
name: '单例实例',
time: Date.now()
};
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
// 2. 使用 ES6 Class 实现单例
class SingletonClass {
constructor() {
// 如果已经存在实例,直接返回
if (SingletonClass.instance) {
return SingletonClass.instance;
}
this.name = '单例实例';
this.time = Date.now();
SingletonClass.instance = this;
// 禁止外部通过 new 创建多个实例
return this;
}
// 静态方法获取实例(可选)
static getInstance() {
if (!SingletonClass.instance) {
SingletonClass.instance = new SingletonClass();
}
return SingletonClass.instance;
}
}
// 使用示例
const s1 = Singleton.getInstance();
const s2 = Singleton.getInstance();
console.log(s1 === s2); // true
const s3 = new SingletonClass();
const s4 = new SingletonClass();
console.log(s3 === s4); // true
应用场景:全局状态管理(Vuex/Redux)、日志记录器、数据库连接池。
1.2 工厂模式(Factory)
定义一个创建对象的接口,由子类决定实例化哪个类。
javascript
// 简单工厂
class Button {
render() {
throw new Error('子类必须实现 render 方法');
}
}
class PrimaryButton extends Button {
render() {
return '<button class="btn-primary">主要按钮</button>';
}
}
class DangerButton extends Button {
render() {
return '<button class="btn-danger">危险按钮</button>';
}
}
class ButtonFactory {
static createButton(type) {
switch (type) {
case 'primary':
return new PrimaryButton();
case 'danger':
return new DangerButton();
default:
throw new Error('未知按钮类型');
}
}
}
// 使用示例
const btn1 = ButtonFactory.createButton('primary');
console.log(btn1.render()); // <button class="btn-primary">主要按钮</button>
const btn2 = ButtonFactory.createButton('danger');
console.log(btn2.render()); // <button class="btn-danger">危险按钮</button>
应用场景:UI 组件库根据类型创建不同组件、解析不同格式的数据(JSON/XML)。
1.3 建造者模式(Builder)
将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
javascript
class Pizza {
constructor() {
this.size = null;
this.cheese = false;
this.pepperoni = false;
this.mushrooms = false;
}
describe() {
return `披萨尺寸: ${this.size}, 奶酪: ${this.cheese}, 意大利辣香肠: ${this.pepperoni}, 蘑菇: ${this.mushrooms}`;
}
}
class PizzaBuilder {
constructor() {
this.pizza = new Pizza();
}
setSize(size) {
this.pizza.size = size;
return this; // 支持链式调用
}
addCheese() {
this.pizza.cheese = true;
return this;
}
addPepperoni() {
this.pizza.pepperoni = true;
return this;
}
addMushrooms() {
this.pizza.mushrooms = true;
return this;
}
build() {
return this.pizza;
}
}
// 使用示例
const pizza = new PizzaBuilder()
.setSize('大')
.addCheese()
.addPepperoni()
.build();
console.log(pizza.describe()); // 披萨尺寸: 大, 奶酪: true, 意大利辣香肠: true, 蘑菇: false
应用场景:构建复杂的配置对象、生成不同风格的报表、组装复杂的 DOM 结构。
二、结构型模式
2.1 适配器模式(Adapter)
将一个类的接口转换成客户希望的另一个接口,使原本不兼容的类可以一起工作。
javascript
// 旧版 API(不兼容的接口)
class OldApi {
fetchData() {
return { code: 200, data: { name: '旧数据', age: 25 } };
}
}
// 新版 API 期望的接口
class NewApi {
getData() {
return { status: 'success', payload: { name: '新数据', age: 30 } };
}
}
// 适配器:将旧 API 适配为新 API 的接口
class ApiAdapter {
constructor(oldApi) {
this.oldApi = oldApi;
}
getData() {
const result = this.oldApi.fetchData();
// 转换数据格式
return {
status: result.code === 200 ? 'success' : 'error',
payload: result.data
};
}
}
// 使用示例
const oldApi = new OldApi();
const adapter = new ApiAdapter(oldApi);
console.log(adapter.getData()); // { status: 'success', payload: { name: '旧数据', age: 25 } }
// 客户端代码无需修改,统一使用 getData()
const newApi = new NewApi();
console.log(newApi.getData()); // { status: 'success', payload: { name: '新数据', age: 30 } }
应用场景:兼容旧版接口、封装第三方库、统一不同数据源的格式。
2.2 装饰器模式(Decorator)
动态地给对象添加额外的职责,比继承更灵活。
javascript
// 基础咖啡类
class Coffee {
cost() {
return 10;
}
description() {
return '基础咖啡';
}
}
// 装饰器基类(也可以直接用函数实现)
class CoffeeDecorator {
constructor(coffee) {
this.coffee = coffee;
}
cost() {
return this.coffee.cost();
}
description() {
return this.coffee.description();
}
}
// 具体装饰器:加牛奶
class MilkDecorator extends CoffeeDecorator {
cost() {
return this.coffee.cost() + 5;
}
description() {
return this.coffee.description() + ' + 牛奶';
}
}
// 具体装饰器:加糖
class SugarDecorator extends CoffeeDecorator {
cost() {
return this.coffee.cost() + 3;
}
description() {
return this.coffee.description() + ' + 糖';
}
}
// 使用示例(组合装饰)
let coffee = new Coffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
console.log(coffee.description()); // 基础咖啡 + 牛奶 + 糖
console.log(coffee.cost()); // 18
// 更简洁的函数式装饰器
function withMilk(fn) {
return function (...args) {
console.log('加了牛奶');
return fn(...args);
};
}
function withSugar(fn) {
return function (...args) {
console.log('加了糖');
return fn(...args);
};
}
const makeCoffee = () => '制作咖啡';
const decorated = withMilk(withSugar(makeCoffee));
console.log(decorated()); // 加了牛奶 加了糖 制作咖啡
应用场景:日志记录、性能监控、权限校验、数据缓存(AOP 编程)。
2.3 代理模式(Proxy)
为其他对象提供一种代理以控制对这个对象的访问。
javascript
// 1. 使用 ES6 Proxy 实现代理
const target = {
name: '敏感数据',
password: '123456'
};
const handler = {
get(obj, prop) {
if (prop === 'password') {
return '*** 无权访问 ***';
}
return obj[prop];
},
set(obj, prop, value) {
if (prop === 'password') {
console.log('不能直接修改密码');
return false;
}
obj[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // 敏感数据
console.log(proxy.password); // *** 无权访问 ***
proxy.password = 'new'; // 不能直接修改密码
// 2. 虚拟代理(图片懒加载)
class ImageLoader {
constructor(src) {
this.src = src;
}
display() {
console.log(`加载图片: ${this.src}`);
}
}
class ProxyImage {
constructor(src) {
this.src = src;
this.realImage = null;
}
display() {
if (!this.realImage) {
console.log('显示占位图...');
this.realImage = new ImageLoader(this.src);
}
this.realImage.display();
}
}
// 使用示例
const img = new ProxyImage('photo.jpg');
img.display(); // 显示占位图... 加载图片: photo.jpg
img.display(); // 加载图片: photo.jpg(已缓存)
应用场景:数据校验、访问控制、缓存代理、图片懒加载、防抖节流。
三、行为型模式
3.1 观察者模式(Observer)
定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会得到通知。
javascript
// 1. 简单观察者模式
class Subject {
constructor() {
this.observers = [];
}
// 添加观察者
attach(observer) {
this.observers.push(observer);
}
// 移除观察者
detach(observer) {
const index = this.observers.indexOf(observer);
if (index > -1) {
this.observers.splice(index, 1);
}
}
// 通知所有观察者
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} 收到通知: ${data}`);
}
}
// 使用示例
const subject = new Subject();
const obs1 = new Observer('观察者1');
const obs2 = new Observer('观察者2');
subject.attach(obs1);
subject.attach(obs2);
subject.notify('状态已更新');
// 观察者1 收到通知: 状态已更新
// 观察者2 收到通知: 状态已更新
// 2. Vue 响应式原理的简化版(发布-订阅)
class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (Dep.target && !this.subscribers.includes(Dep.target)) {
this.subscribers.push(Dep.target);
}
}
notify() {
this.subscribers.forEach(sub => sub());
}
}
// 使用示例
Dep.target = null;
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
dep.depend();
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
dep.notify();
}
}
});
}
const data = {};
defineReactive(data, 'count', 0);
// 模拟 watcher
Dep.target = () => console.log('count 变化了:', data.count);
data.count; // 触发依赖收集
Dep.target = null;
data.count = 1; // count 变化了: 1
应用场景:事件系统(EventEmitter)、Vue 响应式、消息队列、DOM 事件监听。
3.2 策略模式(Strategy)
定义一系列算法,将每个算法封装起来,并使它们可以相互替换。
javascript
// 策略接口(使用对象存储不同策略)
const strategies = {
// 普通会员
normal: (price) => price,
// 会员折扣
member: (price) => price * 0.9,
// VIP 折扣
vip: (price) => price * 0.8,
// 超级 VIP
superVip: (price) => price * 0.7
};
// 上下文:根据策略计算价格
class PriceCalculator {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
calculate(price) {
return this.strategy(price);
}
}
// 使用示例
const calculator = new PriceCalculator(strategies.normal);
console.log(calculator.calculate(100)); // 100
calculator.setStrategy(strategies.vip);
console.log(calculator.calculate(100)); // 80
calculator.setStrategy(strategies.superVip);
console.log(calculator.calculate(100)); // 70
// 直接使用策略函数(更简洁)
function getPrice(price, type) {
return strategies[type] ? strategies[type](price) : price;
}
console.log(getPrice(100, 'member')); // 90
console.log(getPrice(100, 'vip')); // 80
应用场景:表单验证(不同校验规则)、支付方式选择、排序算法切换、动画缓动函数。
3.3 迭代器模式(Iterator)
提供一种方法顺序访问聚合对象中的各个元素,而不暴露其内部表示。
javascript
// 1. 实现自定义迭代器
class MyArray {
constructor(items) {
this.items = items;
}
// 实现 Symbol.iterator 方法
[Symbol.iterator]() {
let index = 0;
const items = this.items;
return {
next() {
if (index < items.length) {
return { value: items[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
}
// 使用示例
const arr = new MyArray([1, 2, 3, 4, 5]);
for (const item of arr) {
console.log(item); // 1, 2, 3, 4, 5
}
// 2. 实现树结构的迭代器(深度优先遍历)
class TreeNode {
constructor(value) {
this.value = value;
this.children = [];
}
addChild(node) {
this.children.push(node);
return this;
}
// 深度优先迭代器
*[Symbol.iterator]() {
yield this.value;
for (const child of this.children) {
yield* child;
}
}
}
// 使用示例
const root = new TreeNode(1);
root.addChild(new TreeNode(2)).addChild(new TreeNode(3));
root.children[0].addChild(new TreeNode(4)).addChild(new TreeNode(5));
for (const val of root) {
console.log(val); // 1, 2, 4, 5, 3
}
应用场景:遍历树形结构、数据流处理、自定义集合遍历。
3.4 职责链模式(Chain of Responsibility)
使多个对象都有机会处理请求,将这些对象连成一条链,并沿着这条链传递请求。
javascript
// 审批流程示例
class Handler {
constructor() {
this.nextHandler = null;
}
setNext(handler) {
this.nextHandler = handler;
return handler; // 支持链式调用
}
handle(request) {
if (this.nextHandler) {
return this.nextHandler.handle(request);
}
return '请求未被处理';
}
}
// 具体处理者:经理(可审批 1000 元以下)
class Manager extends Handler {
handle(request) {
if (request.amount <= 1000) {
return `经理审批通过: ${request.amount}元`;
}
return super.handle(request);
}
}
// 具体处理者:总监(可审批 5000 元以下)
class Director extends Handler {
handle(request) {
if (request.amount <= 5000) {
return `总监审批通过: ${request.amount}元`;
}
return super.handle(request);
}
}
// 具体处理者:总经理(可审批 10000 元以下)
class GeneralManager extends Handler {
handle(request) {
if (request.amount <= 10000) {
return `总经理审批通过: ${request.amount}元`;
}
return '金额过大,需要董事会审批';
}
}
// 使用示例
const manager = new Manager();
const director = new Director();
const gm = new GeneralManager();
manager.setNext(director).setNext(gm);
console.log(manager.handle({ amount: 500 })); // 经理审批通过: 500元
console.log(manager.handle({ amount: 2000 })); // 总监审批通过: 2000元
console.log(manager.handle({ amount: 8000 })); // 总经理审批通过: 8000元
console.log(manager.handle({ amount: 20000 })); // 金额过大,需要董事会审批
// 更简洁的函数式版本(中间件模式)
function createMiddlewareChain(...middlewares) {
return function (context, next) {
let index = 0;
function dispatch(i) {
if (i === middlewares.length) {
return next ? next(context) : context;
}
const middleware = middlewares[i];
return middleware(context, () => dispatch(i + 1));
}
return dispatch(0);
};
}
// 使用示例:Koa 风格中间件
const chain = createMiddlewareChain(
(ctx, next) => { console.log('中间件1 开始'); next(); console.log('中间件1 结束'); },
(ctx, next) => { console.log('中间件2 开始'); next(); console.log('中间件2 结束'); },
(ctx, next) => { console.log('中间件3 执行'); return ctx; }
);
chain({});
// 中间件1 开始
// 中间件2 开始
// 中间件3 执行
// 中间件2 结束
// 中间件1 结束
应用场景:审批流程、中间件(Express/Koa)、表单校验链、事件冒泡。
3.5 发布-订阅模式(Publish-Subscribe)
观察者模式的升级版,通过事件中心解耦发布者和订阅者。
javascript
class EventBus {
constructor() {
// 存储事件和对应的回调
this.events = {};
}
// 订阅事件
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(callback);
// 返回取消订阅函数
return () => this.off(eventName, callback);
}
// 订阅一次
once(eventName, callback) {
const wrapper = (...args) => {
callback(...args);
this.off(eventName, wrapper);
};
return this.on(eventName, wrapper);
}
// 取消订阅
off(eventName, callback) {
if (!this.events[eventName]) return;
this.events[eventName] = this.events[eventName]
.filter(cb => cb !== callback);
}
// 发布事件
emit(eventName, ...args) {
if (!this.events[eventName]) return;
this.events[eventName].forEach(callback => {
try {
callback(...args);
} catch (error) {
console.error(`事件 ${eventName} 执行错误:`, error);
}
});
}
// 清除所有事件
clear() {
this.events = {};
}
}
// 使用示例
const bus = new EventBus();
// 订阅事件
const unsubscribe1 = bus.on('userLogin', (user) => {
console.log(`用户 ${user.name} 登录了`);
});
const unsubscribe2 = bus.on('userLogin', (user) => {
console.log(`记录登录日志: ${user.name}`);
});
// 发布事件
bus.emit('userLogin', { name: 'Alice' });
// 用户 Alice 登录了
// 记录登录日志: Alice
// 取消订阅
unsubscribe1();
bus.emit('userLogin', { name: 'Bob' });
// 记录登录日志: Bob
// 一次性订阅
bus.once('onceEvent', () => console.log('只执行一次'));
bus.emit('onceEvent'); // 只执行一次
bus.emit('onceEvent'); // 不执行
应用场景:跨组件通信(EventBus)、微前端通信、WebSocket 消息分发、全局状态管理。
四、总结
| 模式类型 | 模式名称 | 一句话总结 | 常见场景 |
|---|---|---|---|
| 创建型 | 单例模式 | 全局唯一实例 | 全局状态、日志 |
| 创建型 | 工厂模式 | 统一创建对象 | 组件工厂、解析器 |
| 创建型 | 建造者模式 | 分步构建复杂对象 | 配置对象、表单生成 |
| 结构型 | 适配器模式 | 接口转换兼容 | API 适配、库封装 |
| 结构型 | 装饰器模式 | 动态增强功能 | 日志、缓存、权限 |
| 结构型 | 代理模式 | 控制对象访问 | 懒加载、校验、缓存 |
| 行为型 | 观察者模式 | 状态变更通知 | 响应式、事件系统 |
| 行为型 | 策略模式 | 算法可互换 | 表单校验、支付 |
| 行为型 | 迭代器模式 | 统一遍历方式 | 集合遍历、树遍历 |
| 行为型 | 职责链模式 | 请求链式处理 | 中间件、审批流程 |
| 行为型 | 发布-订阅模式 | 事件中心解耦 | 跨组件通信、消息 |
掌握这些设计模式,不仅能让你的代码更加优雅、可维护,还能在面试中脱颖而出。建议结合项目实践,逐步理解每种模式的适用场景,而不是生搬硬套。