JavaScript 如何实现发布订阅
你有没有过这样的经历:和朋友约好周末一起去看展览,你提前在群里“报名”,而展览主办方只要确定有足够的人参加,就会把时间、地点发在群里通知所有人,不用单独挨个跟你说。这种“一方订阅事件、一方发布通知,消息自动推给订阅者”的模式,就是今天要讲的发布订阅模式,而JavaScript里也能轻松实现它。
什么是发布订阅模式?
先把它拆成两个好懂的部分:“发布”就是有人把消息发出来,“订阅”就是有人提前等着接收这类消息。就像小区的快递通知:你(订阅者)在物业那里留了电话,物业(调度中心)收到快递(发布事件)后,会把取件码发给所有留了电话的业主,你不用天天盯着快递柜,物业也不用挨个敲门喊,两边都省心。

在代码里,它就是三个核心角色:一是“消息中心”,专门管登记谁要接收消息、谁来发消息;二是“订阅者”,负责登记自己要等的消息;三是“发布者”,把消息推给所有登记过的订阅者。
用JS写个简单的发布订阅
我们可以用一个对象当“消息中心”,用它的方法来实现订阅、发布的功能。就像给物业建一个登记本,有业主来登记(订阅),有快递来(发布),登记本就自动把消息传给所有登记的人。
先建这个“消息中心”,它需要存所有订阅的事件和对应的处理函数:
`js
// 消息中心
const eventCenter = {
// 存订阅的事件,键是事件名,值是该事件的处理函数数组
events: {},
// 订阅方法:传事件名和要执行的函数
on(eventName, handler) {
// 如果这个事件还没登记过,就新建一个数组存处理函数
if (!this.events[eventName]) {
this.events[eventName] = [];
}
// 把处理函数加入对应事件的数组里
this.events[eventName].push(handler);
},
// 发布方法:传事件名和要带的数据
emit(eventName, data) {
// 如果这个事件没有订阅者,就直接返回,不做处理
const handlers = this.events[eventName];
if (!handlers) return;
// 遍历所有订阅这个事件的处理函数,把数据传给它们执行
handlers.forEach(handler => handler(data));
}
};
`
现在用这个例子试试:假设你和朋友约了看展览,“展览确定开展”是事件,你和朋友是订阅者,分别登记接收这个事件的通知:
`js
// 订阅者1:你,收到展览消息后要做的事:查路线
eventCenter.on('展览确定开展', (data) => {
console.log(你收到通知:${data},现在查地铁路线);
});
// 订阅者2:朋友,收到消息后要做的事:调休
eventCenter.on('展览确定开展', (data) => {
console.log(朋友收到通知:${data},现在申请调休);
});
// 发布者:展览主办方,发布确定开展的消息
eventCenter.emit('展览确定开展', '本周末展览正常开展,地点在市美术馆');
`
运行这段代码,你会看到两个订阅者的处理函数都被触发了,分别输出对应的内容,就像现实里你和朋友都收到了展览通知。
解决重复订阅和取消订阅的小问题
有时候你可能会不小心给同一个事件登记两次,比如你既在群里又单独找物业登记,就会收到两次通知。我们可以加一个“取消订阅”的功能,帮你注销不用的登记:
`js
eventCenter.off(eventName, handler) {
const handlers = this.events[eventName];
if (!handlers) return;
// 过滤掉要取消的那个处理函数
this.events[eventName] = handlers.filter(h => h !== handler);
}
`
比如你后来发现朋友也会查路线,就可以把自己的订阅取消:
`js
// 先存自己的处理函数
const myHandler = (data) => console.log(你收到通知:${data},现在查地铁路线);
eventCenter.on('展览确定开展', myHandler);
// 后来取消订阅
eventCenter.off('展览确定开展', myHandler);
`
这样再发布展览消息时,你就不会重复收到通知了。
这种模式不用让发布者知道谁会接收消息,也不用让订阅者知道谁会发布,就像现实里的小区通知,两边互不打扰,代码也会更灵活,适合处理多个模块间的消息传递,比如页面里的弹窗通知、数据更新提醒等场景。