文章中英模式
布鲁斯前端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' } }