发布订阅模式还不会??戳这里,50行核心代码,手把手教你学会

小插曲

事件

  • 建议大家看下官网中events事件的描述node中events事件
  • 发布订阅模式定义了一种一对多的依赖关系
  • 在Node中EventEmitter开放on(事件名,回调函数)用于订阅事件
  • emit(事件名)用于发布事件,可能对应多个订阅事件,让订阅事件依次执行

不明白?没关系。举个最简单例子,女人失恋了会哭,还会找新男朋友,在这里哭和找男朋友相当于订阅女人失恋的回调,什么时候执行呢?当发布女人失恋这件事的时候,说的这么抽象,直接来一段代码吧

 

  • 是不是很简单,只有发布这个事件时候,被订阅的事件才会依次执行,形成一对多的依赖关系。接下来直接写源码实现

思路构建

  • 我们想构造一个类似这样的对象 {"失恋":[cry,findBoy]},当事件发布的时候,让数组中对应的函数依次执行,就实现了这样的效果
  • 先讲个小知识点 {}和Object.create(null)区别。 {}有作用链,通过Object.create(null)创造的空对象没有作用链,给大家演示下,其它就没啥区别。源码就是这样写(逼格高)

 

实现events模块

1、on和emit 两个核心方法

  • 源码实现
// 声明EventEmitter事件发生器构造函数
function EventEmitter() {
    this._events = Object.create(null);
}
//on 订阅方法实现  因为在实例上调用,所以写在原型上
EventEmitter.prototype.on = function(type,callback){
    // 如果实例不存在则创建一个空对象,Object.create(null)没有链
    if(!this._events) {
        this._events = Object.create(null);
    }
    if(this._events[type]){ //如果失恋有对应的值,直接往数组push
        this._events[type].push(callback)
    }else { //第一次订阅,没有失恋,就声明{失恋:[cry]}
        this._events[type] = [callback];
    }
};
// emit方法实现
EventEmitter.prototype.emit = function(type){
    if(this._events[type]){ //{失恋:[cry,eat]} 如果失恋对应有值,依次执行里面的方法
        this._events[type].forEach(fn=>fn())
    }
};
module.exports = EventEmitter
复制代码
  • 十几行代码就实现核心功能,这么简单?对 就是这么简单,赶快来测试下吧

 

2、removeListener 取消订阅事件,失恋了不想哭了,所以我们提供个移除监听的方法

  • 比较简单,直接上代码吧看的直接
// 移除订阅事件的方法
EventEmitter.prototype.removeListener = function(type,callback){
    if(this._events[type]){
        // 返回false就表示不要了,用filter实现去重
        this._events[type] = this._events[type].filter(fn=>fn!==callback)
    }
};
复制代码
  • 测试下吧,失恋了不想哭了
  • 完美实现,是不是很激动。

3、removeAllListeners移除全部的监听器,与removeListener相对应

// removeAllListeners 移除所有的监听者
EventEmitter.prototype.removeAllListeners = function(){
//简单粗暴,直接赋值空对象 {}
    this._events = Object.create(null);
};
复制代码
  • 测试下,失恋了既不想哭,也不想找对象,什么也不打印就对拉

 

4、扩展once方法 我们希望哭的事件 多次发布emit时候只执行一次,也就代表执行一次后需要将事件从对应关系中移除掉。

// once实现
EventEmitter.prototype.once = function(type,callback,flag){
    // 先绑定 调用后再删除,运用了one函数 {失恋:one}
    let one = (...args)=> {
        callback(...args);
        this.removeListener(type, one);
    }
    //自定义属性 因为实例中没有one属性
    one.l = callback;
    this.on(type,one)
};
// 移除订阅事件的方法
EventEmitter.prototype.removeListener = function(type,callback){
    if(this._events[type]){
        // 返回false就表示不要了,用filter实现去重
        this._events[type] = this._events[type].filter(fn=>fn!==callback && fn.l!==callback)
    }
};
复制代码
  • 你可能会疑惑为什么声明一个wrap函数,设想下,不然你告诉我怎么先绑定一次,在移除。很多人可能都会这么写
  • 错误例子 错误例子 错误例子(重要事情说三遍)
// - 错误例子 错误例子 错误例子(重要事情说三遍)
//你可能会这么写,但刚绑定就移除拉,体会这意思了吧
EventEmitter.prototype.once = function(type,callback){
//先绑定在移除
    this.on(type,callback);
    this.removeListener(type,callback)
};
复制代码
  • 测试下吧,一步一个脚印

 

5、newListener方法。当cry添加到内部监听数组({失恋:[cry]})之前,会触发自身的'newListener'事件

  • 没听懂?我们先来看官方的用法

简单说就是可以监控到订阅的事件类型,上源码看下如何实现

//on 订阅方法实现  因为在实例上调用,所以写在原型上
EventEmitter.prototype.on = function(type,callback){
    // 如果实例不存在则创建一个空对象,Object.create(null)没有链
    if(!this._events) {
        this._events = Object.create(null);
    }
    if(type!=="newListener"){
        if(this._events["newListener"]){
            this._events["newListener"].forEach(fn=>fn(type))
        }
    }
    if(this._events[type]){ //如果失恋有对应的值,直接往数组push
        this._events[type].push(callback)
    }else { //第一次订阅,没有失恋,就声明{失恋:[cry]}
        this._events[type] = [callback];
    }
};
复制代码
  • 测试下吧

 

看到这里,基本方法都实现了。不常用就不解释拉。 如果大家想看所有源码方法解析,可以点进我github上参考


作者:言sir
链接:https://juejin.im/post/5b338118e51d4558a75e9921
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/sinat_17775997/article/details/84935640