魯斯前端布魯斯前端

文章中英模式

布魯斯前端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' } }