用 Map 实现发布-订阅模式的核心是利用其键可为任意类型、避免原型污染、支持高效增删查的特性;初始化 Map 存储事件名到回调数组的映射,提供 on(订阅)、emit(触发,浅拷贝执行)、off(取消订阅,支持全删或指定删除)方法。

用 Map 实现发布-订阅(Pub/Sub)模式的事件监听管理,核心是利用 Map 的键值对结构存储事件名与回调函数列表的映射关系,相比对象或普通数组更安全、更灵活,支持任意类型作为事件名(包括对象、函数),且天然避免原型链污染。
用 Map 存储事件名与回调列表的映射
Map 适合做事件中心的底层容器,因为:
- 键可以是任意类型(字符串、Symbol、对象等),不局限于字符串;
- 可直接通过
map.get(key)快速获取对应事件的所有监听器; - 删除事件监听时,可精准操作某事件的回调数组,不影响其他事件。
初始化一个空 Map,每个键是事件名(event),值是该事件绑定的所有回调函数组成的数组:
实现 on(订阅/监听)方法
把回调函数推入对应事件名的数组中。若事件名首次出现,先初始化空数组:
if (!eventMap.has(event)) {
eventMap.set(event, []);
}
eventMap.get(event).push(callback);
}
实现 emit(发布/触发)方法
遍历指定事件名对应的回调数组,依次执行,并传入参数。注意需拷贝一份数组,防止监听器内部调用 off 导致遍历时数组长度变化:
const callbacks = eventMap.get(event);
if (!callbacks || callbacks.length === 0) return;
// 浅拷贝,避免执行中修改原数组影响遍历
[...callbacks].forEach(cb => cb(...args));
}
实现 off(取消订阅)方法
支持两种方式:移除某个事件的所有监听器,或只移除指定回调:
function off(event, callback) {if (!eventMap.has(event)) return;
if (callback === undefined) {
// 移除整个事件的所有监听
eventMap.delete(event);
} else {
// 只移除指定回调
const callbacks = eventMap.get(event);
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
}
这样就完成了一个轻量、健壮、支持任意事件名类型的发布-订阅系统。它不依赖外部库,语义清晰,也便于扩展(比如加 once、priority、context 支持等)。


















