JavaScript设计模式
介绍
设计模式是在软件开发中反复出现的问题的解决方案,是经过验证的最佳实践。JavaScript设计模式有助于提高代码的可维护性、可复用性和可扩展性,是高级前端开发人员必须掌握的核心知识点之一。
原理
设计模式的核心原理:
- 封装变化:将可能变化的部分封装起来,使系统的其他部分不受影响
- 单一职责:每个模块或类只负责一个功能
- 开放封闭:对扩展开放,对修改封闭
- 依赖倒置:依赖抽象,不依赖具体实现
- 接口隔离:使用多个专门的接口,而不是一个统一的接口
在JavaScript中,设计模式通常基于对象原型和闭包特性实现,而不是传统的类继承。
图示
// 设计模式基本结构示例
// 单例模式
const Singleton = (function() {
let instance;
function createInstance() {
const object = new Object({name: 'Singleton'});
return object;
}
return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
实例
单例模式
单例模式确保一个类只有一个实例,并提供一个全局访问点。
// 闭包实现的单例模式
const Singleton = (function() {
let instance;
function createInstance() {
// 私有构造函数
function PrivateSingleton() {
this.timestamp = new Date().getTime();
this.id = Math.random().toString(36).substring(2, 10);
}
// 公共方法
PrivateSingleton.prototype.getInfo = function() {
return { id: this.id, created: this.timestamp };
};
return new PrivateSingleton();
}
return {
// 全局访问点
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
},
// 重置单例(通常用于测试)
resetInstance: function() {
instance = null;
}
};
})();
// 使用示例
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // 输出: true
console.log(instance1.getInfo()); // 输出: { id: '随机字符串', created: 时间戳 }
工厂模式
工厂模式用于创建对象,隐藏对象的创建细节,使代码更加灵活和可维护。
// 工厂方法模式
class Product {
constructor(name, price) {
this.name = name;
this.price = price;
}
getDetails() {
return `${this.name} - ¥${this.price}`;
}
}
class ElectronicsProduct extends Product {
constructor(name, price, warranty) {
super(name, price);
this.warranty = warranty; // 保修年限
this.category = 'electronics';
}
getDetails() {
return `${super.getDetails()} (保修${this.warranty}年)`;
}
}
class ClothingProduct extends Product {
constructor(name, price, size) {
super(name, price);
this.size = size;
this.category = 'clothing';
}
getDetails() {
return `${super.getDetails()} (尺码: ${this.size})`;
}
}
// 抽象工厂类
class ProductFactory {
createProduct(type, options) {
throw new Error('子类必须实现createProduct方法');
}
}
// 具体工厂实现
class ConcreteProductFactory extends ProductFactory {
createProduct(type, options) {
switch(type) {
case 'electronics':
return new ElectronicsProduct(options.name, options.price, options.warranty);
case 'clothing':
return new ClothingProduct(options.name, options.price, options.size);
default:
throw new Error(`不支持的产品类型: ${type}`);
}
}
}
// 使用示例
const factory = new ConcreteProductFactory();
const laptop = factory.createProduct('electronics', {
name: '笔记本电脑',
price: 6999,
warranty: 2
});
console.log(laptop.getDetails()); // 输出: 笔记本电脑 - ¥6999 (保修2年)
const tshirt = factory.createProduct('clothing', {
name: 'T恤',
price: 99,
size: 'L'
});
console.log(tshirt.getDetails()); // 输出: T恤 - ¥99 (尺码: L)
观察者模式
观察者模式定义了对象间的一种一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。
class Observer {
constructor(name) {
this.name = name;
}
update(subject, data) {
console.log(`${this.name} 收到更新:`, data);
}
}
class Subject {
constructor() {
this.observers = new Map(); // 使用Map存储观察者,便于管理
this.state = { count: 0 };
}
// 添加观察者
attach(observer, observerId) {
if (!observerId) {
observerId = Math.random().toString(36).substring(2, 10);
}
this.observers.set(observerId, observer);
return observerId;
}
// 移除观察者
detach(observerId) {
return this.observers.delete(observerId);
}
// 通知所有观察者
notify(data = {}) {
// 合并状态数据
const updateData = { ...this.state, ...data };
// 更新内部状态
this.state = updateData;
// 通知所有观察者
for (const observer of this.observers.values()) {
observer.update(this, updateData);
}
}
// 更新状态并通知观察者
setState(newState) {
this.state = { ...this.state, ...newState };
this.notify();
}
}
// 使用示例
const subject = new Subject();
const observer1 = new Observer('数据可视化组件');
const observer2 = new Observer('日志记录器');
const id1 = subject.attach(observer1);
const id2 = subject.attach(observer2);
subject.setState({ count: 10 });
// 输出:
// 数据可视化组件 收到更新: { count: 10 }
// 日志记录器 收到更新: { count: 10 }
subject.notify({ action: 'refresh' });
// 输出:
// 数据可视化组件 收到更新: { count: 10, action: 'refresh' }
// 日志记录器 收到更新: { count: 10, action: 'refresh' }
subject.detach(id1);
subject.setState({ count: 20 });
// 输出:
// 日志记录器 收到更新: { count: 20, action: 'refresh' }
装饰器模式
装饰器模式允许向一个现有对象添加新的功能,同时又不改变其结构。
// 基础组件
class Coffee {
constructor() {
this.description = '原味咖啡';
}
getDescription() {
return this.description;
}
cost() {
return 30;
}
}
// 装饰器基类
class CoffeeDecorator {
constructor(coffee) {
this.coffee = coffee;
}
getDescription() {
return this.coffee.getDescription();
}
cost() {
return this.coffee.cost();
}
}
// 具体装饰器 - 牛奶
class MilkDecorator extends CoffeeDecorator {
getDescription() {
return `${this.coffee.getDescription()}, 加牛奶`;
}
cost() {
return this.coffee.cost() + 5;
}
}
// 具体装饰器 - 糖
class SugarDecorator extends CoffeeDecorator {
getDescription() {
return `${this.coffee.getDescription()}, 加糖`;
}
cost() {
return this.coffee.cost() + 2;
}
}
// 具体装饰器 - 巧克力
class ChocolateDecorator extends CoffeeDecorator {
getDescription() {
return `${this.coffee.getDescription()}, 加巧克力`;
}
cost() {
return this.coffee.cost() + 8;
}
}
// 使用示例
let coffee = new Coffee();
console.log(`${coffee.getDescription()} - ¥${coffee.cost()}`); // 输出: 原味咖啡 - ¥30
coffee = new MilkDecorator(coffee);
console.log(`${coffee.getDescription()} - ¥${coffee.cost()}`); // 输出: 原味咖啡, 加牛奶 - ¥35
coffee = new SugarDecorator(coffee);
console.log(`${coffee.getDescription()} - ¥${coffee.cost()}`); // 输出: 原味咖啡, 加牛奶, 加糖 - ¥37
coffee = new ChocolateDecorator(coffee);
console.log(`${coffee.getDescription()} - ¥${coffee.cost()}`); // 输出: 原味咖啡, 加牛奶, 加糖, 加巧克力 - ¥45
专业解决方案
常用设计模式分类
- 创建型模式:工厂模式、单例模式、建造者模式、原型模式
- 结构型模式:适配器模式、装饰器模式、代理模式、外观模式
- 行为型模式:观察者模式、策略模式、命令模式、迭代器模式
设计模式应用场景
- 单例模式:适用于需要全局唯一实例的场景,如配置管理、日志记录
- 工厂模式:适用于对象创建逻辑复杂或需要根据不同条件创建不同对象的场景
- 观察者模式:适用于事件处理系统、GUI组件交互、消息订阅发布系统
- 装饰器模式:适用于需要动态扩展对象功能而不修改原有代码的场景
- 策略模式:适用于需要根据不同条件选择不同算法或行为的场景
最佳实践
- 不要过度使用设计模式,简单问题用简单方法解决
- 理解设计模式的本质,而不是死记硬背实现代码
- 结合JavaScript的特性选择合适的设计模式实现方式
- 在团队中保持一致的设计模式使用风格
- 考虑性能和内存占用,避免不必要的复杂性
工具推荐
- Pattern Lab:设计模式库和工具
- Refactoring Guru:设计模式学习资源
- JSPatterns:JavaScript设计模式特定资源
- ESLint-plugin-design-patterns:检查代码中设计模式使用的ESLint插件