原生JavaScript DOM提供的自定义事件API和自己实现的发布订阅模式有啥区别?

第一种方式:使用JavaScript原生提供的CustomEvent创建自定义事件
比如可以这样创建:

var evt = new CustomEvent(type, {detail: msg, bubbles: true, cancelable: true});

然后监听并触发某个自定义事件

element.addEventListener('longpress', longpressFn);
element.dispatchEvent(evt);

第二种方式:自己模拟一个自定义事件发布订阅对象

var Event = (function() {
    var clientList = {};
    var listen,
        trigger,
        remove;
    listen = function(key, fn) {
        if (!clientList[key]) {
            clientList[key] = [];
        }
        clientList[key].push(fn);
    };

    trigger = function() {
        var key = [].shift.call(arguments);
        var fns = clientList[key];

        if (!fns || fns.length === 0) {
            return false;
        }

        for (var i = 0, fn; fn = fns[i++];) {
            fn.apply(this, arguments);
        }
    };


    remove = function(key, fn) {
        var fns = clientList[key];

        // key对应的消息没有被人订阅
        if (!fns) {
            return false;
        }

        // 没有传入fn(具体的回调函数), 表示取消key对应的所有订阅
        if (!fn) {
            fns && (fns.length = 0);
        }
        else {
            // 反向遍历
            for (var i = fns.length - 1,_fn=fns[i]; i >= 0; i--) {
                if (_fn === fn) {
                    // 删除订阅回调函数
                    fns.splice(i, 1);
                }
            }
        }
    };

    return {
        listen: listen,
        trigger: trigger,
        remove: remove
    }
}());

这两种方式都能实现一个自定义事件的创建、监听、触发、删除,但区别是第一种是原生DOM提供的API,第二种是用发布订阅模式去模拟的?原生DOM的创建方式也是一种发布订阅模式么?
那在实际项目中用哪种比较好?查了下资料,发现用原生DOM的API貌似有IE方面的兼容问题
现在一些大型的库或者框架都是用第二种那样自己实现的自定义事件订阅系统么?(主要最近在造自己的轮子,听说这种模式可以降低代码的耦合度,想学习下。。)

阅读 4.2k
3 个回答

你的订阅发布确定没问题么?我理解的,订阅发布结合面相对象来说,最简单的模式应该是至少有三部分:

发布消息者,消息本身,和订阅者。

原生dom的事件模式中,其实本身也就是一个订阅发布模式。

1.每个htmlElement类最基础的interface,都是eventTarget,也就是"发布消息者"。这个你可以看mdn:这里
也就是说,HTMLElement有个继承链,Element---Node---EventTarget。EvetntTarget接口实现了发送消息的方法,所以,原生dom元素可以发送消息,也就是说,dom元素可以作为"发布消息者"。

2.每次事件发送的消息本身,其实按照面相对象来说,每次事件就是一个Event类的实例,每次有事件触发,都会创建一个消息Event实例,由发布消息者,也就是指定的dom元素,广播给订阅者。

3.dom模型中的订阅者,其实可以简单理解成就是我们注册好的"事件处理函数"。然而,实际上,按照dom标准,dom事件订阅者也是有个interface格式的:这里。满足了这个格式,就可以给eventTarget添加listener。每次有Event发出,不管你是自定义事件还是真的用户交互事件,都会触发listener。

你自己实现的那个,并没有抽象出这三个部分吧。当然,订阅发布完全可以自己订阅自己,消息也只在内部不暴露。只是感觉这样就很模糊了。

其实,你自己实现的订阅发布,每个接口原生JS的HTMLElement上都有了。只看最新的,你自己实现的那个listen方法,就是addEventListener;trigger方法,就是dispatchEvent;remove就是removeEventListener。而且你自己实现的那个并没有抽象出Event消息和订阅者。

呃,题主连设计模式和 API 的关系都没搞清就要开始造轮子了么……

这两者的关系就是没有关系。设计模式是设计模式,任何语言任何 API,想用就用,它是“可复用面向对象软件的基础”。浏览器 DOM 事件机制是“订阅模式”的一种实现,你想自己实现一个订阅模式当然没问题(不过多半是观察者模式)。

建议题主先把《设计模式》那本书好好看一遍。

自己写的没有办法实现异步,除非使用setTimeout模拟。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题