跳到主要内容

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插件