3行代码一个订阅发布中心

2021/08/03 21:47
阅读数 174

原文链接:https://juejin.cn/post/6991992950876028959

前言

无处不在的订阅发布模式,也是常备手写系列,可见其地位。

其实,在浏览器端,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" })

输出结果

pic_cddff47c.png

是不是有点小惊喜。

原理浅析

window是表象,根源是 EventTarget。

其一共三个方法,也正是这三个方法,让其自身是一个订阅发布中心:

  • EventTarget.addEventListener()
    在EventTarget上注册特定事件类型的事件处理程序。
  • EventTarget.removeEventListener()
    EventTarget中删除事件侦听器。
  • EventTarget.dispatchEvent()
    将事件分派到此EventTarget。

Window的继承关系:

pic_a1699eec.png

Document的继承关系:

pic_b92dbda1.png

Element的继承关系:

pic_8d78c380.png

所以document和window对象均是一个订阅发布中心。

更重要的是我们常用的div, span, input等等nodeType为1的元素节点,也统统是一个订阅发布中心。

假如你连nodeType也不清楚,没关系,请参见Node.nodeType。

当前版本的问题

  1. 不能多实例化啊
  2. 挂载window上太丑了
  3. 不能多参数啊
    答:要那么多参数干嘛
  4. 参数从ev.detail上获取
    答:还好
  5. 不能在node, web worker中使用啊
    答:node要你写吗, 自带啊。web worker嘛,确实。
  6. ........

到这里,我不生气,真的不生气, 特定场景解决特定问题就完毕了。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" })

完美,名字好看了,使用方便了,也支持多实例了。

结果

pic_5006647d.png

总结

是不是很简单,其实不用第三方库,你也可以拥有订阅发布中心,就这么简单。

要是都看到这了,都不点个赞,那就是你的不对了。


本文分享自微信公众号 - JavaScript忍者秘籍(js-obok)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部