如何理解JavaScript设计模式_单例模式和观察者模式怎样实现

单例模式确保类唯一实例并提供全局访问,观察者模式实现一对多响应式通信;二者可结合为单例事件总线,兼顾唯一性与松耦合通信。

单例模式确保一个类只有一个实例,并提供全局访问点;观察者模式定义对象间一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会自动收到通知。两者都用于解耦和复用,但解决的问题不同:单例关注“唯一性”,观察者关注“响应式通信”。

单例模式:怎么保证只创建一次?

核心是控制构造逻辑,不让外部随意 new 实例。常见做法是用闭包或静态属性缓存实例,首次调用时创建,之后直接返回已有实例。

  • 用闭包封装私有实例变量,暴露一个获取实例的方法
  • ES6 类写法中可在类内部用 static 属性存储实例,constructor 检查是否已存在
  • 注意:单例不是“全局变量”,而是受控的、延迟初始化的唯一对象
  • 典型场景:日志器、配置管理器、状态仓库(如 Redux store 的初始化)

观察者模式:如何让多个对象监听同一事件?

关键角色有“被观察者(Subject)”和“观察者(Observer)”。Subject 维护一个观察者列表,提供订阅(subscribe)、取消订阅(unsubscribe)、通知(notify)三个基本操作。

  • 订阅时把回调函数或观察者对象加入列表
  • 触发事件时遍历列表,依次执行每个观察者的回调
  • 可扩展支持事件类型(如 on('click', fn))、传递参数、异步通知等
  • 现代实践中常被 EventEmitter、RxJS、Vue/React 的响应式系统替代,但原理一致

两个模式可以一起用吗?

可以。比如用单例实现一个全局事件总线(EventBus),它本身是唯一的,同时具备观察者功能:任何模块都能 subscribe 到它,也能 emit 事件通知其他模块。

  • 这样既避免多个事件中心导致混乱,又实现松耦合通信
  • 注意避免内存泄漏:订阅后记得在合适时机 unsubscribe
  • 实际项目中,优先考虑框架内置方案(如 Vue 的 $emit / $on,或 React 的 Context + useReducer)

不复杂但容易忽略细节:单例要防多线程(JS 单线程所以通常不用考虑),观察者要注意执行顺序和错误隔离。理解本质比死记代码更重要。