核心是让订阅者容器实现symbol.iterator接口以支持原生遍历,而非直接暴露数组;通过封装类并部署[symbol.iterator]()方法,使for...of、扩展运算符等自然生效,提升可读性与兼容性。

在自定义事件总线中,用 Iterator 管理订阅者列表,核心不是“必须用迭代器”,而是利用 Symbol.iterator 让订阅者集合支持原生遍历能力,从而让 for...of、扩展运算符、解构等语法自然生效,提升代码可读性与兼容性。关键在于:把订阅者容器设计成可迭代对象,而非直接暴露数组。
订阅者容器实现可迭代接口
不直接导出内部数组(如 this.handlers = []),而是封装为类,并部署 [Symbol.iterator]() 方法:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
class EventHandlerList {
constructor() {
this._handlers = [];
}
add(handler) {
if (typeof handler === 'function') {
this._handlers.push(handler);
}
}
remove(handler) {
const i = this._handlers.indexOf(handler);
if (i > -1) this._handlers.splice(i, 1);
}
// ✅ 关键:返回一个迭代器对象
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index
事件总线中集成并安全遍历
在触发事件时,用 for...of 遍历订阅者——这比 forEach 更安全,因为迭代过程中若某 handler 调用 off() 删除自身或其他 handler,原生数组 forEach 会跳过后续项或报错;而自定义迭代器可控制遍历逻辑(例如先拷贝再遍历):
- 推荐做法:迭代前浅拷贝,避免遍历时修改原数组影响迭代
- 可在
[Symbol.iterator]中返回[...this._handlers][Symbol.iterator](),但更高效的是在总线的emit内部处理 - 示例:
for (const handler of [...this.events.get(type) || []]) { handler(data); }
配合 WeakMap 实现按事件类型隔离 + 迭代友好
用 WeakMap 存储各事件类型的处理器列表,既避免内存泄漏(key 是对象时),又保持结构清晰。每个值仍是 EventHandlerList 实例:
class EventBus {
constructor() {
this.events = new WeakMap(); // key: event type string, value: EventHandlerList
}
on(type, handler) {
let list = this.events.get(type);
if (!list) {
list = new EventHandlerList();
this.events.set(type, list);
}
list.add(handler);
}
emit(type, data) {
const list = this.events.get(type);
if (list) {
// ✅ 自然支持 for...of、Array.from、[...list]
for (const handler of list) {
try {
handler(data);
} catch (e) {
console.error('Event handler error:', e);
}
}
}
}
}
实际收益:不只是语法糖
- 外部可直接用
Array.from(bus.events.get('click'))获取数组,无需暴露私有字段 - 调试时
console.log([...bus.events.get('save')])清晰可见当前所有监听器 - 便于未来升级:比如改为惰性过滤迭代器(只遍历满足条件的 handler),只需改
[Symbol.iterator],调用方无感知 - 与标准库工具链对齐:
find、some、filter等方法可基于Array.from()或扩展运算符无缝使用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










