前端常用设计模式有哪些?
- 观察者模式
- 发布订阅模式
- 单例模式
- 工厂模式
- 适配器模式
- 装饰器模式
- 代理模式
- 策略模式
- 模板方法模式
- 命令模式
- 职责链模式
- 组合模式
- 享元模式
1.观察者模式:
观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者(观察者)都会自动收到通知并更新。在前端开发中,观察者模式常用于实现组件间的通信和数据更新。
interface Observer {
update: (data: any) => void;
}
interface Subject {
addObserver: (observer: Observer) => void;
removeObserver: (observer: Observer) => void;
notifyObservers: (data: any) => void;
}
class ConcreteSubject implements Subject {
private observers: Observer[] = [];
addObserver(observer: Observer) {
this.observers.push(observer);
}
removeObserver(observer: Observer) {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
notifyObservers(data: any) {
this.observers.forEach(observer => observer.update(data));
}
}
class ConcreteObserver implements Observer {
update(data: any) {
console.log(`Received data: ${data}`);
}
}
const subject = new ConcreteSubject();
const observer1 = new ConcreteObserver();
const observer2 = new ConcreteObserver();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notifyObservers('Hello World!');
subject.removeObserver(observer2);
subject.notifyObservers('Goodbye World!');
2.发布订阅模式:
发布订阅模式也是一种行为型设计模式,它定义了一种一对多的依赖关系,但与观察者模式不同的是,发布订阅模式中的发布者和订阅者之间没有直接的联系,而是通过一个消息代理(或称为事件中心)来进行通信。当发布者发布消息时,消息代理会将消息广播给所有订阅者,订阅者收到消息后执行相应的操作。在前端开发中,发布订阅模式常用于解耦组件间的关系,实现松耦合的架构。
class EventEmitter {
private events: {[key: string]: Function[]} = {};
on(eventName: string, callback: Function) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(callback);
}
emit(eventName: string, ...args: any[]) {
const callbacks = this.events[eventName];
if (callbacks) {
callbacks.forEach(callback => {
callback(...args);
});
}
}
off(eventName: string, callback: Function) {
const callbacks = this.events[eventName];
if (callbacks) {
this.events[eventName] = callbacks.filter(cb => cb !== callback);
}
}
}
const emitter = new EventEmitter();
emitter.on('event1', (arg1, arg2) => {
console.log(`event1 with ${arg1} and ${arg2}`);
});
emitter.on('event2', (arg1, arg2) => {
console.log(`event2 with ${arg1} and ${arg2}`);
});
emitter.emit('event1', 'foo', 'bar');
emitter.emit('event2', 'baz', 'qux');
emitter.off('event1', (arg1, arg2) => {
console.log(`event1 with ${arg1} and ${arg2}`);
});
emitter.emit('event1', 'foo', 'bar');
3.单例模式:
单例模式是一种设计模式,它限制了类的实例化次数只能一次。在前端开发中,单例模式可以用于管理全局状态、缓存数据等场景。
class Singleton {
private static instance: Singleton;
private constructor() {}
public static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
export default Singleton;
4.工厂模式:
工厂模式是一种设计模式,它提供了一种创建对象的方式,将对象的创建和使用分离开来。在前端开发中,工厂模式可以用于创建复杂的组件、封装第三方库等场景。
// 工厂模式示例
interface Product {
name: string;
price: number;
}
class ConcreteProductA implements Product {
name = 'Concrete Product A';
price = 50;
}
class ConcreteProductB implements Product {
name = 'Concrete Product B';
price = 100;
}
class ProductFactory {
createProduct(type: 'A' | 'B'): Product {
switch (type) {
case 'A':
return new ConcreteProductA();
case 'B':
return new ConcreteProductB();
default:
throw new Error('Invalid product type.');
}
}
}
const factory = new ProductFactory();
const productA = factory.createProduct('A');
const productB = factory.createProduct('B');
console.log(productA); // Concrete Product A { name: 'Concrete Product A', price: 50 }
console.log(productB); // Concrete Product B { name: 'Concrete Product B', price: 100 }
5.适配器模式:
适配器模式是一种设计模式,它将一个类的接口转换成客户端所期望的另一种接口,从而使原本不兼容的类能够一起工作。在前端开发中,适配器模式可以用于兼容不同版本的 API、封装第三方组件等场景。
class Adaptee {
public specificRequest(): string {
return 'Adaptee specific request';
}
}
interface Target {
request(): string;
}
class Adapter implements Target {
private adaptee: Adaptee;
constructor(adaptee: Adaptee) {
this.adaptee = adaptee;
}
public request(): string {
return `Adapter: (TRANSLATED) ${this.adaptee.specificRequest()}`;
}
}
const clientCode = (target: Target) => {
console.log(target.request());
};
const adaptee = new Adaptee();
console.log('Client: The Adaptee class has a weird interface. See, I don\'t understand it:');
console.log(`Adaptee: ${adaptee.specificRequest()}`);
console.log('Client: But I can work with it via the Adapter:');
const adapter = new Adapter(adaptee);
clientCode(adapter);
6.装饰器模式:
装饰器模式是一种设计模式,它允许在不改变对象自身的基础上,动态地给对象添加新的功能。在前端开发中,装饰器模式可以用于添加日志、性能监控、权限控制等功能。
6.1 方法装饰器:
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Arguments: ${args}`);
const result = originalMethod.apply(this, args);
console.log(`Result: ${result}`);
return result;
};
return descriptor;
}
class Example {
@log
public method(arg1: string, arg2: number): string {
return `Method: ${arg1} ${arg2}`;
}
}
const example = new Example();
example.method('hello', 123);
6.2 访问器的装饰器
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.get;
descriptor.get = function() {
console.log(`Getting ${key} with value ${original.call(this)}`);
return original.call(this);
}
return descriptor;
}
class Example {
private _name: string;
constructor(name: string) {
this._name = name;
}
@log
get name() {
return this._name;
}
}
const example = new Example('John');
console.log(example.name); // Getting name with value John, John
6.3 属性的装饰器
function nameDecorator(target: any, key: string): any {
const descriptor: PropertyDescriptor = {
writable: true,
};
return descriptor;
}
class Test {
@nameDecorator
name = 'Monday';
}
const test = new Test();
test.name = 'Tuesday';
console.log(test.name); // Tuesday
6.4 参数装饰器
// 原型,方法名,参数所在的位置
function paramDecorator(target: any, key: string, paramIndex: number): any {
console.log(target, key, paramIndex); // Test { getInfo: [Function] } , 'getInfo' , 1(参数所在位置是第2个位置)
}
class Test {
getInfo(name: string, @paramDecorator age: number) {
console.log(name, age);
}
}
const test = new Test();
test.getInfo('Monday', 18); // Monday 18
7.代理模式
代理模式是一种设计模式,它为其他对象提供一种代理以控制对这个对象的访问。在前端开发中,代理模式可以用于实现图片懒加载、缓存数据、控制访问权限等场景。
interface Subject {
request(): void;
}
class RealSubject implements Subject {
public request(): void {
console.log("RealSubject: Handling request.");
}
}
class Proxy implements Subject {
private realSubject: RealSubject;
constructor(realSubject: RealSubject) {
this.realSubject = realSubject;
}
public request(): void {
if (this.checkAccess()) {
this.realSubject.request();
this.logAccess();
}
}
private checkAccess(): boolean {
console.log("Proxy: Checking access prior to firing a real request.");
return true;
}
private logAccess(): void {
console.log("Proxy: Logging the time of request.");
}
}
function clientCode(subject: Subject) {
subject.request();
}
console.log("Client: Executing the client code with a real subject:");
const realSubject = new RealSubject();
clientCode(realSubject);
console.log("");
console.log("Client: Executing the same client code with a proxy:");
const proxy = new Proxy(realSubject);
clientCode(proxy);
8.策略模式:
策略模式是一种设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用它们的客户端,从而可以在不修改客户端的情况下动态地切换算法。在前端开发中,策略模式可以用于实现表单验证、排序算法、动画效果等场景。
interface Strategy {
execute(a: number, b: number): number;
}
class Add implements Strategy {
execute(a: number, b: number): number {
return a + b;
}
}
class Subtract implements Strategy {
execute(a: number, b: number): number {
return a - b;
}
}
class Multiply implements Strategy {
execute(a: number, b: number): number {
return a * b;
}
}
class Calculator {
private strategy: Strategy;
constructor(strategy: Strategy) {
this.strategy = strategy;
}
setStrategy(strategy: Strategy) {
this.strategy = strategy;
}
executeStrategy(a: number, b: number): number {
return this.strategy.execute(a, b);
}
}
const calculator = new Calculator(new Add());
console.log(calculator.executeStrategy(10, 5)); // Output: 15
calculator.setStrategy(new Subtract());
console.log(calculator.executeStrategy(10, 5)); // Output: 5
calculator.setStrategy(new Multiply());
console.log(calculator.executeStrategy(10, 5)); // Output: 50
9.模板方法模式
模板方法模式是一种设计模式,它定义了一个算法的骨架,将一些步骤延迟到子类中实现。模板方法模式使得子类可以在不改变算法结构的情况下重新定义算法中的某些步骤。在前端开发中,模板方法模式可以用于实现组件的生命周期、异步请求等场景。
abstract class AbstractClass {
public templateMethod(): void {
this.baseOperation1();
this.requiredOperations1();
this.baseOperation2();
this.hook1();
this.requiredOperation2();
this.baseOperation3();
this.hook2();
}
protected baseOperation1(): void {
console.log('AbstractClass says: I am doing the bulk of the work');
}
protected baseOperation2(): void {
console.log('AbstractClass says: But I let subclasses override some operations');
}
protected baseOperation3(): void {
console.log('AbstractClass says: But I am doing the bulk of the work anyway');
}
protected abstract requiredOperations1(): void;
protected abstract requiredOperation2(): void;
protected hook1(): void {}
protected hook2(): void {}
}
class ConcreteClass1 extends AbstractClass {
protected requiredOperations1(): void {
console.log('ConcreteClass1 says: Implemented Operation1');
}
protected requiredOperation2(): void {
console.log('ConcreteClass1 says: Implemented Operation2');
}
}
class ConcreteClass2 extends AbstractClass {
protected requiredOperations1(): void {
console.log('ConcreteClass2 says: Implemented Operation1');
}
protected requiredOperation2(): void {
console.log('ConcreteClass2 says: Implemented Operation2');
}
protected hook1(): void {
console.log('ConcreteClass2 says: Overridden Hook1');
}
}
function clientCode(abstractClass: AbstractClass) {
abstractClass.templateMethod();
}
console.log('Same client code can work with different subclasses:');
clientCode(new ConcreteClass1());
console.log('');
console.log('Same client code can work with different subclasses:');
clientCode(new ConcreteClass2());
10.命令模式:
命令模式是一种设计模式,用于实现撤销、重做、菜单、快捷键等场景。。在前端开发中,命令模式可以成对象,从而使得可以用不同的请求对客户端进行参数化。命令模式可以将请求的发送者和接收者解耦,使得请求的发送者不需要知道请求的接收。
// 命令接口
interface Command {
execute(): void;
}
// 具体命令类
class ConcreteCommand implements Command {
private receiver: Receiver;
constructor(receiver: Receiver) {
this.receiver = receiver;
}
execute(): void {
this.receiver.action();
}
}
// 接收者类
class Receiver {
public action(): void {
console.log("接收者的action()方法被调用...");
}
}
// 调用者类
class Invoker {
private command: Command;
constructor(command: Command) {
this.command = command;
}
public setCommand(command: Command): void {
this.command = command;
}
public run(): void {
console.log("Invoker开始执行命令...");
this.command.execute();
}
}
// 客户端代码
let receiver: Receiver = new Receiver();
let command: Command = new ConcreteCommand(receiver);
let invoker: Invoker = new Invoker(command);
invoker.run();
11.职责链模式:
职责链模式是一种设计模式,它将请求的发送者和接收者解耦,使得多个对象都有机会处理这个请求。将这些对象串成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。在前端开发中,职责链模式可以用于处理用户输入、事件冒泡等场景。
// 职责链模式示例
interface Handler {
setNext(handler: Handler): Handler;
handle(request: string): string;
}
abstract class AbstractHandler implements Handler {
private nextHandler: Handler;
public setNext(handler: Handler): Handler {
this.nextHandler = handler;
return handler;
}
public handle(request: string): string {
if (this.nextHandler) {
return this.nextHandler.handle(request);
}
return null;
}
}
class Handler1 extends AbstractHandler {
public handle(request: string): string {
if (request === 'handler1') {
return `Handled by Handler1`;
}
return super.handle(request);
}
}
class Handler2 extends AbstractHandler {
public handle(request: string): string {
if (request === 'handler2') {
return `Handled by Handler2`;
}
return super.handle(request);
}
}
class Handler3 extends AbstractHandler {
public handle(request: string): string {
if (request === 'handler3') {
return `Handled by Handler3`;
}
return super.handle(request);
}
}
const handler1 = new Handler1();
const handler2 = new Handler2();
const handler3 = new Handler3();
handler1.setNext(handler2).setNext(handler3);
console.log(handler1.handle('handler2')); // Output: Handled by Handler2
12.组合模式:
组合模式是一种设计模式,它允许将对象组合成树形结构来表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性,从而可以忽略对象与组合对象之间的差异。在前端开发中,组合模式可以用于实现菜单、树形控件等场景。
// 抽象组件
abstract class Component {
protected name: string;
constructor(name: string) {
this.name = name;
}
abstract add(component: Component): void;
abstract remove(component: Component): void;
abstract display(depth: number): void;
}
// 叶子组件
class Leaf extends Component {
constructor(name: string) {
super(name);
}
add(component: Component): void {
console.log("Leaf cannot add component");
}
remove(component: Component): void {
console.log("Leaf cannot remove component");
}
display(depth: number): void {
console.log("-".repeat(depth) + this.name);
}
}
// 容器组件
class Composite extends Component {
private children: Component[] = [];
constructor(name: string) {
super(name);
}
add(component: Component): void {
this.children.push(component);
}
remove(component: Component): void {
const index = this.children.indexOf(component);
if (index !== -1) {
this.children.splice(index, 1);
}
}
display(depth: number): void {
console.log("-".repeat(depth) + this.name);
this.children.forEach((child) => {
child.display(depth + 2);
});
}
}
// 使用示例
const root = new Composite("root");
root.add(new Leaf("Leaf A"));
root.add(new Leaf("Leaf B"));
const composite = new Composite("Composite X");
composite.add(new Leaf("Leaf XA"));
composite.add(new Leaf("Leaf XB"));
root.add(composite);
const composite2 = new Composite("Composite XY");
composite2.add(new Leaf("Leaf XYA"));
composite2.add(new Leaf("Leaf XYB"));
composite.add(composite2);
root.add(new Leaf("Leaf C"));
root.display(1);
13.享元模式:
享元模式是一种设计模式,它通过共享对象来减少内存的使用和提高性能。享元模式将对象分为内部状态和外部状态,内部状态可以被共享,而外部状态则需要在使用时传入。在前端开发中,享元模式可以用于优化大量相似对象的创建和管理,例如在 Canvas 中绘制大量图形时,可以使用享元模式来共享相同的图形元素。
class ChessPiece {
constructor(color) {
this.color = color;
}
getPosition() {
return this.position;
}
setPosition(position) {
this.position = position;
}
getColor() {
return this.color;
}
}
class ChessPieceFactory {
constructor() {
this.pool = {};
}
getChessPiece(color) {
if (this.pool[color]) {
return this.pool[color];
} else {
const chessPiece = new ChessPiece(color);
this.pool[color] = chessPiece;
return chessPiece;
}
}
}
const chessPieceFactory = new ChessPieceFactory();
const blackChessPiece1 = chessPieceFactory.getChessPiece('black');
const blackChessPiece2 = chessPieceFactory.getChessPiece('black');
const whiteChessPiece1 = chessPieceFactory.getChessPiece('white');
const whiteChessPiece2 = chessPieceFactory.getChessPiece('white');
console.log(blackChessPiece1 === blackChessPiece2); // true,黑色棋子共享
console.log(whiteChessPiece1 === whiteChessPiece2); // true,白色棋子共享
console.log(blackChessPiece1 === whiteChessPiece1); // false,不同颜色棋子不共享