FORMA

设计模式

常见创建型、结构型、行为型模式在 JavaScript 中的实现示例。对象创建与继承见 原型与面向对象

一、创建型模式

创建型模式关注对象如何被创建,隐藏创建逻辑,提升代码灵活性和复用性。

1. 单例模式(Singleton)

定义:确保一个类只有一个实例,并提供一个全局访问点。

实现:利用闭包或 ES6 模块(模块本身就是单例)。

js
// 方式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)

定义:定义一个创建对象的接口,由子类或具体工厂类决定实例化哪个类。简单工厂、工厂方法和抽象工厂。

简单工厂示例

js
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 原生的对象创建模式。

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)

定义:将一个类的接口转换成客户端期望的另一个接口,使原本不兼容的类可以一起工作。

js
// 旧接口
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 差异(如 XMLHttpRequestfetch)、第三方库接口统一。

2. 装饰器模式(Decorator)

定义:动态地给对象添加额外职责,比继承更灵活。在 React 中高阶组件(HOC)就是装饰器思想。

js
// 基础函数
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 示例

js
function withAuth(Component) {
    return function Authenticated(props) {
        if (!props.isLoggedIn) return <LoginPage />;
        return <Component {...props} />;
    };
}

应用场景:日志增强、权限控制、性能监控。

3. 代理模式(Proxy)

定义:为其他对象提供一种代理以控制对这个对象的访问。JavaScript 的 Proxy 对象直接实现了该模式。

js
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)与事件总线

定义:发布者发布事件,订阅者监听事件,彼此解耦。不同于观察者模式(下面介绍),发布-订阅通常有一个事件中心作为调度器。

js
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 实现

js
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)

定义:定义一系列算法,把它们封装起来,并使它们可以相互替换。算法的变化独立于使用它的客户端。

js
// 策略集
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)。

js
// 自定义可迭代对象
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)

定义:将请求封装成对象,从而使您可以用不同的请求对客户进行参数化、排队或记录日志,以及支持可撤销操作。

js
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)

定义:使多个对象都有机会处理请求,将这些对象连成一条链,并沿着链传递请求,直到有一个对象处理它为止。

js
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代理模式实现基础