前端常用设计模式大全

一、创建型模式

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 适配、库封装
结构型 装饰器模式 动态增强功能 日志、缓存、权限
结构型 代理模式 控制对象访问 懒加载、校验、缓存
行为型 观察者模式 状态变更通知 响应式、事件系统
行为型 策略模式 算法可互换 表单校验、支付
行为型 迭代器模式 统一遍历方式 集合遍历、树遍历
行为型 职责链模式 请求链式处理 中间件、审批流程
行为型 发布-订阅模式 事件中心解耦 跨组件通信、消息

掌握这些设计模式,不仅能让你的代码更加优雅、可维护,还能在面试中脱颖而出。建议结合项目实践,逐步理解每种模式的适用场景,而不是生搬硬套。

相关推荐
张文君21 分钟前
ubuntu26.04坏块分区隔离急速版
java·前端·python
两只羊ovo23 分钟前
给 Agent 装上手脚:手写 mini-Cursor,从建项目到跑起来
前端
学长毕业设计32 分钟前
基于SpringBoot的校园爱心志愿管理系统(源码+文档+讲解视频)
vue.js·spring boot·后端
Hive_MOM34 分钟前
制造企业库存精益化管理数字化:从“库存压资金、找货靠人翻“到“库存水位实时可见、资金周转提速“
前端·制造
光影少年42 分钟前
react navite调试方案:Flipper、远程调试
前端·javascript·react native·react.js·前端框架
计算机魔术师1 小时前
智谱开源 GLM-5.3 模型权重,主打智能体编程与网络防御
前端
百变梦仔1 小时前
多个 Skill 同时出现时,Codex 前端任务我会先定优先级
前端
IT_陈寒1 小时前
SpringBoot自动配置失效?这个隐式依赖坑了我三天
前端·人工智能·后端
码视野1 小时前
基于 Spring Boot + Vue3 的【城市雨污水管网液位淤积溯源与立交桥下穿隧洞防汛排涝智控中台】设计与实现(含PRD/三端高保真源码/大屏)
java·前端·人工智能·spring boot·后端