JavaScript设计模式

张玥 2026年2月14日 阅读时间 50分钟
设计模式 JavaScript 架构 代码设计 前端工程化
JavaScript设计模式概念图

设计模式是软件工程中针对常见问题的可重用解决方案,在JavaScript开发中尤为重要。无论你是在构建简单的交互组件还是大型单页应用,掌握设计模式都能让你的代码更优雅、更易维护。本文将从JavaScript的语言特性出发,全面讲解创建型、结构型和行为型三大类共12种核心设计模式,并通过大量代码示例和实际应用场景帮助你深入理解,真正达到50分钟沉浸式学习的深度。

设计模式概述与分类

设计模式起源于建筑学,后被引入软件工程。在JavaScript中,由于原型继承、函数式特性等语言特点,一些模式的实现方式可能与其他语言有所不同。通常将23种经典模式分为三大类:

// 三大分类概览
/* 创建型 (Creational) */
• 工厂模式 (Factory)
• 单例模式 (Singleton)
• 建造者模式 (Builder)
• 原型模式 (Prototype)

/* 结构型 (Structural) */
• 适配器模式 (Adapter)
• 装饰器模式 (Decorator)
• 代理模式 (Proxy)
• 外观模式 (Facade)

/* 行为型 (Behavioral) */
• 观察者模式 (Observer)
• 策略模式 (Strategy)
• 迭代器模式 (Iterator)
• 命令模式 (Command)
📌 创建型 — 处理对象创建机制,提高灵活性和复用性。
📌 结构型 — 关注类和对象的组合,构建更大结构的同时保持灵活高效。
📌 行为型 — 涉及对象间的职责分配和通信,刻画复杂的控制流。

工厂模式 (Factory)

工厂模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。在JavaScript中通常用函数来封装对象创建逻辑。

// 简单工厂:根据类型返回不同对象
function createUser(type, name) {
  const user = { name };
  if (type === 'admin') {
    user.role = 'admin';
    user.permissions = ['read', 'write', 'delete'];
  } else if (type === 'guest') {
    user.role = 'guest';
    user.permissions = ['read'];
  }
  return user;
}

const admin = createUser('admin', 'Alice');
console.log(admin.permissions); // ['read','write','delete']
  • 优点:解耦对象的创建和使用;符合开放封闭原则。
  • 缺点:会增加系统复杂度,工厂类集中所有创建逻辑。
  • 适用:需要根据不同条件创建不同实例的场景(如弹窗类型、日志记录器)。

单例模式 (Singleton)

确保一个类只有一个实例,并提供全局访问点。在JS中常用闭包或模块模式实现。

// 使用立即执行函数和闭包保存实例
const Singleton = (function() {
  let instance;
  function init() {
    return {
      data: '单例数据',
      method() { console.log(this.data); }
    };
  }
  return {
    getInstance() {
      if (!instance) instance = init();
      return instance;
    }
  };
})();
const a = Singleton.getInstance();
const b = Singleton.getInstance();
console.log(a === b); // true
🎯 应用场景:全局状态管理(如Redux store)、配置对象、日志模块、缓存实例。
⚠️ 注意:多线程环境需加锁,但在JS中通常为单线程,闭包足够。

建造者模式 (Builder)

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。适合参数较多的对象。

// 建造者模式示例:构造HTTP请求对象
class RequestBuilder {
  constructor() {
    this.url = '';
    this.method = 'GET';
    this.headers = {};
    this.body = null;
  }
  setUrl(url) { this.url = url; return this; }
  setMethod(method) { this.method = method; return this; }
  setHeader(key, val) { this.headers[key] = val; return this; }
  setBody(body) { this.body = body; return this; }
  build() {
    return { url: this.url, method: this.method, headers: this.headers, body: this.body };
  }
}
const request = new RequestBuilder()
  .setUrl('/api/users')
  .setMethod('POST')
  .setHeader('Content-Type', 'application/json')
  .setBody({ name: '张三' })
  .build();

链式调用让代码可读性极高。适用于构建复杂对象如DOM元素、SQL查询、表单配置等。

原型模式 (Prototype)

通过克隆已有对象来创建新对象,避免重复初始化。JavaScript的原型继承本身就是原型模式的体现。

// 使用Object.create实现原型克隆
const proto = {
  greet() { return `你好,我是${this.name}`; }
};
const obj1 = Object.create(proto);
obj1.name = '李四';
console.log(obj1.greet()); // 你好,我是李四

// 也可以使用 structuredClone 深克隆 (现代JS)
const original = { data: [1,2,3] };
const clone = structuredClone(original);
性能优化:当创建对象成本高(如数据库查询)时,通过克隆可节省资源。JS天然支持原型链。

适配器模式 (Adapter)

将一个类的接口转换成客户希望的另一个接口。使原本不兼容的类可以协同工作。

// 旧接口与期待的新接口不匹配
class OldApi { request() { return '旧数据格式'; } }
class NewApi { fetchData() { return '新数据格式'; } }

class Adapter {
  constructor(api) { this.api = api; }
  request() {
    const data = this.api.fetchData();
    return `适配器转换: ${data}`;
  }
}
const adapter = new Adapter(new NewApi());
console.log(adapter.request()); // 适配器转换: 新数据格式
应用:兼容第三方库、API版本升级、统一不同数据源的接口格式。

装饰器模式 (Decorator)

动态地给对象添加额外的职责,比生成子类更灵活。在JS中可用高阶函数或装饰器语法(提案)实现。

// 使用高阶函数装饰
function logDecorator(fn) {
  return function(...args) {
    console.log(`调用函数 ${fn.name},参数:`, args);
    return fn.apply(this, args);
  };
}
function add(a, b) { return a + b; }
const decoratedAdd = logDecorator(add);
decoratedAdd(3, 4); // 日志输出
经典场景:日志记录、性能分析、权限校验、React高阶组件(HOC)。

代理模式 (Proxy)

为另一个对象提供一个替身或占位符以控制对这个对象的访问。ES6的Proxy对象完美实现此模式。

// 使用Proxy实现属性访问验证
const target = { name: 'target', secret: 'confidential' };
const handler = {
  get(obj, prop) {
    if (prop === 'secret') throw new Error('禁止访问');
    return obj[prop];
  }
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // target
// proxy.secret; // Error: 禁止访问
应用:虚拟代理(图片懒加载)、缓存代理、保护代理、远程代理。

观察者模式 (Observer)

定义对象间的一种一对多依赖关系,当一个对象状态改变时,所有依赖者都会收到通知。发布/订阅是其常见变体。

// 简单发布订阅实现
class EventBus {
  constructor() { this.events = {}; }
  on(event, fn) { (this.events[event] ||= []).push(fn); }
  emit(event, data) { this.events[event]?.forEach(fn => fn(data)); }
}
const bus = new EventBus();
bus.on('message', msg => console.log('收到:', msg));
bus.emit('message', 'Hello World'); // 收到: Hello World
这是前端最常用的模式,事件监听、Vue的响应式、Redux等都基于此。

策略模式 (Strategy)

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

// 表单校验策略
const strategies = {
  isNonEmpty: val => !val ? '不能为空' : '',
  minLength: (val, len) => val.length < len ? `至少${len}字符` : ''
};
class Validator {
  config = [];
  add(value, rule, ...args) {
    this.config.push({ value, rule, args });
  }
  validate() {
    return this.config.map(item => strategies[item.rule](item.value, ...item.args)).filter(msg => msg);
  }
}
// 使用: 将算法与调用分离
消除冗长的条件分支,提高可维护性。例如:不同用户的折扣计算、排序算法、动画缓动函数。

迭代器模式 (Iterator)

提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部表示。ES6内置迭代器协议。

// 自定义可迭代对象
const range = {
  from: 1, to: 5,
  [Symbol.iterator]() {
    let current = this.from;
    const last = this.to;
    return {
      next() {
        return current <= last ? { value: current++, done: false } : { done: true };
      }
    };
  }
};
for (let num of range) console.log(num); // 1,2,3,4,5
统一遍历接口,让数据结构(数组、Set、Map)可以被for...of消费。

命令模式 (Command)

将请求封装成对象,从而支持参数化、队列、日志、撤销等操作。

// 命令模式实现撤销
class Calculator {
  value = 0;
  history = [];
  execute(cmd) { this.value = cmd.execute(this.value); this.history.push(cmd); }
  undo() {
    const cmd = this.history.pop();
    if (cmd) this.value = cmd.undo(this.value);
  }
}
class AddCommand { constructor(amount) { this.amount = amount; }
  execute(val) { return val + this.amount; }
  undo(val) { return val - this.amount; }
}
常用于编辑器(撤销重做)、事务处理、宏录制。

综合案例:可配置弹窗系统

将工厂、单例、观察者、策略等模式组合,实现一个灵活的通知弹窗。

// 1. 单例管理弹窗容器
const popupManager = (function() {
  let instance;
  class Popup { /* ... */ }
  return { getInstance() { return instance || (instance = new Popup()); } };
})();
// 2. 工厂创建不同类型弹窗
// 3. 观察者监听关闭事件
// 4. 策略决定动画效果
设计模式不是孤立使用的,优秀的架构往往是多种模式的有机组合。掌握模式的核心思想,而非死记硬背。

如何选择设计模式

没有万能模式,需要根据具体场景、可维护性、团队习惯来定。以下是简要指南:

创建型 —— 当对象创建复杂或需隐藏创建逻辑时选用。
结构型 —— 当需要组合类/对象形成更大结构且希望保持灵活时。
行为型 —— 当关注对象间的通信与职责分配时。

最后记住:模式是经验的总结,不要为了模式而模式。保持代码简洁才是第一要义。