用观察者模式编写一个可被其他对象拓展复用自定义事件系统

2,419 阅读4分钟

观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知

什么是观察者模式?

发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。在 JavaScript 开发中,我们一般用事件模型来替代传统的发布—订阅模式。

举个栗子:

卖水果的张老板和王老板都要进一批香蕉,他们的水果都是在一个叫钱多多(下面统称为钱老板)的水果批发商那里进的。当张老板和王老板到钱老板那里进水果的时候,钱老板告诉张老板和王老板,香蕉还没有到货,得过几天才到货。无奈之下,张老板和王老板都把他们的电话号码留在了钱老板那里,嘱咐钱老板,香蕉到货后,第一时间通知他们。

上面钱老板就扮演了发布者的角色,张老板和王老板则扮演的是订阅者角色。在香蕉到货后,钱老板会主动给张老板和王老发消息,让两位老板来取香蕉。这样的好处就是:香蕉没到的这段时间,张老板和王老板可以做其他的事情,不用主动联系钱老板,只需等待钱老板的消息即可。也就是程序代码中的时间上解耦,对象间解耦。

自定义事件

其实观察者模式我们都曾使用过,就是我们熟悉的事件
但是内置的事件很多时候不能满足我们的要求
所以我们需要自定义事件


现在我们想实现这样的功能 定义一个事件对象,它有以下功能

  • 监听事件(订阅事件)
  • 触发事件(事件发布)
  • 移除事件(取消订阅事件)

当然我们不可能只订阅一个事件,可能会有很多
所以我们要针对不同的事件设置不同的”键”
这样我们储存事件的结构应该是这样的

EventList = {
    evtName1: [回调函数1,回调函数2,...],
    evtName2: [回调函数1,回调函数2,...],
    evtName3: [回调函数1,回调函数2,...],
}

代码如下

var createEventSys = function(){
    return {
        // 通过on接口监听事件eventName
        // 如果事件eventName被触发,则执行callback回调函数
        on: function (eventName, callback) {
            //如果Event对象没有handles属性,则给Event对象定义属性handles,初始值为{}
            //handles属性是用来存储事件和回调执行函数的(即存储订阅的事件和触发事件后执行的相应函数方法)
            if(!this.handles){
                this.handles={};
            }
            //如果handles中不存在事件eventName,则将事件存储在handles中,同时初始化该事件对应的回调逻辑函数集合
            if(!this.handles[eventName]){
                this.handles[eventName]=[];
            }
            //往handles中的eventName对应的回调逻辑函数集合push回调函数callback
            this.handles[eventName].push(callback);
        },
        // 触发事件 eventName
        emit: function (eventName) {
            //如果事件eventName有订阅者,则依次执行事件eventName的订阅者相应的回调方法
           if(this.handles[arguments[0]]){
               for(var i=0;i<this.handles[arguments[0]].length;i++){
                   this.handles[arguments[0]][i](arguments[1]);
               }
           }
        },
        //移除事件 eventName
        remove: function (eventName, fn) {
            //判断事件eventName是否存在fn这个观察者,如果有,则移除事件eventName的fn观察者
            if(this.handles[eventName]){
                for(var i=0; i<this.handles[eventName].length; i++){
                    if(this.handles[eventName][i] === fn){
                        this.handles[eventName].splice(i,1);
                        break;
                    }
                }
            }
        }
    };
}
var Event = createEventSys();
Event.on('test', function (result) {
    console.log(result);
});
Event.on('test', function () {
    console.log('test');
});
Event.emit('test', 'hello world'); // 输出 'hello world' 和 'test'

//对象person1和对象person2拓展复用自定义系统
var person1 = {};
var person2 = {};
Object.assign(person1, createEventSys());
Object.assign(person2, createEventSys());
person1.on('call1', function () {
    console.log('person1');
});
person2.on('call2', function () {
    console.log('person2');
});
person1.emit('call1'); // 输出 'person1'
person1.emit('call2'); // 没有输出
person2.emit('call1'); // 没有输出
person2.emit('call2'); // 输出 'person2'

如上面代码这样,我们用观察者模式就实现了一个基本完善的自定义事件系统。

总结

观察者模式有两个明显的优点

时间上解耦
对象间解耦

在前端开发中,很多地方都适合用观察者模式来做,在适当的地方善用观察者模式

希望这篇文章对大家有帮助,喜欢的话,请关注我,我会持续更新一些技术文章到我的掘金主页,谢谢大家支持!