关于[SSD系列]
专门为8月征文活动而建立的系列,收集整理前端一些有意思或者我们不重视的内容,可能是部分掘友不知道的。
前言
无处不在的订阅发布模式,也是常备手写系列,可见其地位。
其实,在浏览器端,3行代码,没错3行代码,你就可以拥有一个具备订阅,取消订阅,发布,并具备once能力的订阅发布中心。
4行代码的订阅发布中心
订阅发布中心代码
window._on = window.addEventListener; window._off = window.removeEventListener; window._emit = (type, data) => window.dispatchEvent(new CustomEvent(type, { detail: data }));; window._once = (type, callback) => window.addEventListener(type, callback, { once: true, capture: true }); 复制代码
标题是3行也是可以的,把1,2行合并一下:
(window._on = window.addEventListener, window._off = window.removeEventListener); 复制代码
测试代码
function onEventX(ev) { console.log("event-x 收到数据:", ev.detail); } // 订阅 window._on("event-x", onEventX); window._once("event-once", ev => console.log("event-once 收到数据:", ev.detail)); // once window._emit("event-once", { uid: -100, message: "you love me" }); window._emit("event-once", { uid: -100, message: "you love me" }); // 订阅和取消订阅 window._emit("event-x", { uid: 100, message: "i love you" }) window._off("event-x", onEventX); window._emit("event-x", { uid: 100, message: "i love you" }) 复制代码
输出结果
是不是有点小惊喜。
原理浅析
window是表象,根源是 EventTarget。
其一共三个方法,也正是这三个方法,让其自身是一个订阅发布中心:
- EventTarget.addEventListener()
在EventTarget上注册特定事件类型的事件处理程序。 - EventTarget.removeEventListener()
EventTarget中删除事件侦听器。 - EventTarget.dispatchEvent()
将事件分派到此EventTarget。
Window的继承关系:
Document的继承关系:
Element的继承关系:
所以document和window对象均是一个订阅发布中心。
更重要的是我们常用的div, span, input等等nodeType为1的元素节点,也统统是一个订阅发布中心。
假如你连nodeType也不清楚,没关系,请参见Node.nodeType。
当前版本的问题
- 不能多实例化啊
- 挂载window上太丑了
- 不能多参数啊
答:要那么多参数干嘛 - 参数从ev.detail上获取
答:还好 - 不能在node, web worker中使用啊
答:node要你写吗, 自带啊。 web worker嘛,确实。 - ........
到这里,我不生气,真的不生气, 特定场景解决特定问题就完毕了。 1毛钱还想上天啊。
但是就算是1毛钱,我也希望大家获得更好的体验,所以要改造升级。
3以及之后的别想了,我们把1,2点进行完美升级。
升级
升级后的代码会多一点,2倍吧,一共6行代码。
代码
class EventEmitter extends EventTarget { on = this.addEventListener; off = this.removeEventListener; emit = (type, data) => this.dispatchEvent(new CustomEvent(type, { detail: data })); once = (type, callback) => this.on(type, callback, { once: true, capture: true }); } 复制代码
测试代码
var emitter = new EventEmitter(); function onEventX(ev) { console.log("event-x 收到数据:", ev.detail); } // 订阅 emitter.on("event-x", onEventX); emitter.once("event-once", ev => console.log("event-once 收到数据:", ev.detail)); // 发布 emitter.emit("event-once", { uid: -100, message: "you love me" }); emitter.emit("event-once", { uid: -100, message: "you love me" }); emitter.emit("event-x", { uid: 100, message: "i love you" }) emitter.off("event-x", onEventX); emitter.emit("event-x", { uid: 100, message: "i love you" }) 复制代码
完美,名字好看了,使用方便了,也支持多实例了。
结果
总结
是不是很简单,其实不用第三方库,你也可以拥有订阅发布中心,就这么简单。
要是都看到这了,都不点个赞,那就是你的不对了。