前端工程化 · 62/90
1. Babel 的工作原理 2. body-parser 中间件的作用 3. Babel 转译原理 4. 浏览器和 Node 中的事件循环区别 5. 职责链模式 6. 链模式 7. 命令模式 8. 组件封装设计 9. 数据统计 10. dependencies 和 devDependencies 的区别 11. CommonJS 和 ES6 模块引入的区别 12. 设计模式分类 13. 前端开发中常用的设计模式 14. 设计模式应用场景 15. 设计原则 16. 开发环境搭建要点 17. Electron 理解 18. 前后端分离是什么 19. 工厂模式 20. 前端代码重构 21. 前端组件化 22. 前端工程师职业发展 23. 前端工程化方向 24. 前端工程化的理解 25. 前端工程价值体现 26. 前端工程化 27. Git 常用命令与工作流 28. Gulp 任务自动化工具 29. 图片导出 30. 前端模块化规范 31. 迭代器模式 32. JavaScript 编码规范 33. 前端 CI/CD 流程 34. jQuery 生态对比 35. jQuery 实现原理 36. jQuery 与 Sizzle 选择器集成 37. Koa 中间件异常处理 38. jQuery 源码优秀实践 39. jQuery 与 Zepto 对比 40. jQuery UI 自定义组件 41. Koa 中间件不调用 await next() 的影响 42. Koa 在没有 async/await 时如何实现洋葱模型 43. Koa 和 Express 的区别 44. Koa 洋葱模型 45. 登录实现 46. 中介者模式 47. 模块模式 48. 小程序架构 49. 小程序常见问题 50. Monorepo 概念与工具 51. mpvue 框架 52. MVC vs MVP vs MVVM 53. Node.js ES Module 为什么必须加文件扩展名 54. MVC、MVP 和 MVVM 架构模式 55. Node.js 全局对象 56. Node.js 性能监控与优化 57. Node.js 多进程与进程通讯 58. Node.js 调试方法 59. Node.js 中的 process 对象 60. Node.js 的理解与应用场景 61. npm 是什么? 62. 观察者模式和发布订阅模式的区别 63. 页面重构方法 64. PM2 守护进程原理 65. 分页功能的前后端设计 66. PostCSS 作用 67. 项目管理方法 68. Rollup 打包工具 69. 高质量前端代码 70. JavaScript 单例模式实现 71. SSG 静态网站生成 72. 模板方法模式 73. 设计模式的六大原则 74. Tree Shaking 原理 75. 用户授权信息获取流程 76. Vite 原理与性能优势 77. Web App vs Hybrid App vs Native App 78. Web 前端开发注意事项 79. Web APP 设计原则 80. Webpack 构建流程 81. Hash vs ChunkHash vs ContentHash 82. Webpack 热更新原理 83. Webpack Loader 与 Plugin 区别 84. webpack 的 module、bundle、chunk 是什么 85. Webpack Proxy 工作原理与跨域解决 86. webpack、rollup、parcel 的选择 87. WePy 与 mpvue 对比 88. WXML 和 WXSS 89. Webpack Scope Hoisting 90. Zepto 实现原理

观察者模式和发布订阅模式的区别

理解观察者模式和发布订阅模式的定义、实现和使用场景

问题

观察者模式和发布订阅模式分别是什么?它们有什么区别?

解答

观察者模式

观察者模式中,观察者直接订阅主题对象。当主题状态改变时,会直接通知所有观察者。

// 主题(被观察者)
class Subject {
  constructor() {
    this.observers = [];
  }

  // 添加观察者
  addObserver(observer) {
    this.observers.push(observer);
  }

  // 移除观察者
  removeObserver(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }

  // 通知所有观察者
  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 observer1 = new Observer('观察者1');
const observer2 = new Observer('观察者2');

subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify('状态改变了');

发布订阅模式

发布订阅模式中,发布者和订阅者不直接通信,而是通过调度中心(事件中心)进行解耦。

// 调度中心(事件中心)
class EventBus {
  constructor() {
    this.events = {};
  }

  // 订阅事件
  subscribe(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callback);
  }

  // 发布事件
  publish(eventName, data) {
    if (this.events[eventName]) {
      this.events[eventName].forEach(callback => callback(data));
    }
  }

  // 取消订阅
  unsubscribe(eventName, callback) {
    if (this.events[eventName]) {
      this.events[eventName] = this.events[eventName].filter(cb => cb !== callback);
    }
  }
}

// 使用
const eventBus = new EventBus();

// 订阅者1
eventBus.subscribe('userLogin', (data) => {
  console.log('订阅者1 收到登录事件:', data);
});

// 订阅者2
eventBus.subscribe('userLogin', (data) => {
  console.log('订阅者2 收到登录事件:', data);
});

// 发布者发布事件
eventBus.publish('userLogin', { username: 'Alice' });

主要区别

角色数量不同:观察者模式只有观察者和被观察者两个角色,它们直接通信。发布订阅模式有发布者、订阅者和调度中心三个角色,发布者和订阅者通过调度中心间接通信。

耦合程度不同:观察者模式中,主题和观察者相互知道对方的存在,耦合度较高。发布订阅模式中,发布者和订阅者完全解耦,互不知道对方的存在。

使用场景不同:观察者模式适合单个应用内部使用,比如 Vue 的响应式系统。发布订阅模式适合跨应用或跨模块的场景,比如 Node.js 的 EventEmitter、浏览器的事件系统。

关键点

  • 观察者模式是主题直接通知观察者,两者直接耦合
  • 发布订阅模式通过调度中心解耦发布者和订阅者
  • 观察者模式适合应用内部,发布订阅模式适合跨应用场景
  • 发布订阅模式更灵活,但增加了系统复杂度