鲁斯前端布鲁斯前端

文章中英模式

布鲁斯前端JS面试题目 - 实现订阅与发布模式

学习如何实现 JavaScript 中的订阅与发布(Subscribe & Emit)模式,掌握事件驱动程序设计的核心概念,提升前端代码的解耦与可维护性。

影片縮圖

懒得看文章?那就来看视频吧

订阅与发布模式概述

订阅与发布模式(Pub/Sub Pattern)是一种常见的设计模式,它允许不同组件之间通过事件机制进行通信,而不需要彼此之间直接引用。这种模式能够降低组件间的耦合度,提高代码的可维护性和可扩展性。

核心概念

  • 1. 发布者(Publisher/Emitter):负责发出事件的对象
  • 2. 订阅者(Subscriber/Listener):监听并响应事件的对象
  • 3. 事件(Event):由发布者发出的消息,通常包含事件名称和相关数据
  • 4. 事件总线(Event Bus):负责管理订阅关系和分发事件的中间层

JavaScript 生态系统中广泛使用此模式,比如 DOM 事件、Node.js 的 EventEmitter、Vue 的事件系统等。

基本的 EventEmitter 实现

下面是一个简单的 EventEmitter 实现,包含最基本的订阅(on/subscribe)和发布(emit)功能:

class EventEmitter {
  constructor() {
    // 存储事件与对应的监听器
    this.events = {};
  }
  
  // 订阅事件
  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    
    this.events[event].push(listener);
    return this; // 支持链式调用
  }
  
  // 发布事件
  emit(event, ...args) {
    const listeners = this.events[event];
    
    if (!listeners || listeners.length === 0) {
      return false;
    }
    
    listeners.forEach(listener => {
      listener.apply(this, args);
    });
    
    return true;
  }
}

// 使用示例
const emitter = new EventEmitter();

// 订阅事件
emitter.on('userLoggedIn', user => {
  console.log(`用户 ${user.name} 已登录`);
});

emitter.on('userLoggedIn', user => {
  updateUIForLoggedInUser(user);
});

// 发布事件
emitter.emit('userLoggedIn', { name: 'John', id: 123 });
// 输出: 用户 John 已登录

实际应用场景

以下是两个订阅与发布模式的简单应用示例:

1. 简单的通知系统

// 建立事件管理器
const notifier = new EventEmitter();

// 订阅不同类型的通知
notifier.on('message', data => {
  console.log(`收到消息: ${data.text}`);
  showNotification(data.text);
});

notifier.on('error', data => {
  console.error(`错误: ${data.message}`);
  showErrorAlert(data.message);
});

// 在应用程序中发送通知
function sendMessage(text) {
  notifier.emit('message', { text, time: new Date() });
}

function reportError(message) {
  notifier.emit('error', { message, code: 500 });
}

// 使用示例
sendMessage('欢迎使用本系统');
reportError('连接失败');

2. 简易状态管理

// 简单的状态管理器
const store = new EventEmitter();
const state = { count: 0, user: null };

// 订阅状态变化
store.on('stateChange', (newState) => {
  console.log('状态已更新:', newState);
  renderUI(newState);
});

// 更新状态的方法
function updateState(changes) {
  Object.assign(state, changes);
  store.emit('stateChange', state);
}

// 使用示例
function increment() {
  updateState({ count: state.count + 1 });
}

function login(user) {
  updateState({ user });
}

// 触发状态变化
increment(); // 状态已更新: { count: 1, user: null }
login({ name: 'Alice' }); // 状态已更新: { count: 1, user: { name: 'Alice' } }