什么是发布/订阅模式?相信大家在看Vue的视图更新和$emit事件源码的时候,都能看出发布和订阅的身影。包括NodejsEvents和jQuery,也有发布订阅模式。用网上一张比较形象的图:左边是观察者模式,右边是发布订阅模式:定义了几个带有事件名的订阅者集,用于维护(增删改查),执行发布(emit)时通过唯一的事件名(key)一一对应订阅者集合中的方法。具体实现vareventEmitter={list:{},//订阅主题on:function(event,fn){if(typeoffn!=="function"){returnfalse;}//创建订阅者列表,如果存在则直接Insert(this.list[event]||(this.list[event]=[])).push(fn);归还这个;},//发布话题emit:function(){varevent=[].shift.call(参数);if(this.list[event]&&this.list[event].length){varfns=this.list[event].slice();//浅拷贝后直接订阅所有list函数顺序执行for(variinfns){this.list[event][i].apply(this,arguments);}返回这个;}返回假;},}on和emit比较简单首先定义一个list对象使用的映射表,用来存储事件的集合。调用on事件绑定时,判断传入的事件名当前是否存在于列表中。如果不存在,先设置一个空数组,或者直接push。emit发布并执行对应的event事件处理输入的参数(shift切出要触发的事件名),先通过事件名浅拷贝一份list,然后遍历并执行对应的所有函数listthis.list[event][i].apply(this,arguments)//移除对应的订阅者remove:function(event,fn){varfns=this.list[event];如果(!fns)返回false;//如果对应的订阅没有下发If(!fn){deletethis.list[event];归还这个;}//找到对应的订阅者删除,包括一次订阅者for(vari=0;i<=fns.length;i++){if(fns[i]===fn||fns.fn===fn){fns.splice(i,1);休息;}}返回这个;},//创建一个订阅者,执行后立即销毁once(event,fn){functiononce(){this.remove(event,once);fn.apply(这个,参数);}//存储当前fn副本,用于删除查找一次.fn=fn;this.on(事件,一次);归还这个;},};remove删除事件先获取fns主题对应的函数列表做一些判断,如果删除列表中没有指定函数(函数引用)则默认删除对应的整个列表。如果传递了fn,它会在循环中被相应的函数引用。fns.fn===fn在删除once函数时使用。订阅者被封装成一个闭包函数,订阅者fn放在订阅者的once函数属性下。当相应的订阅者执行时,它首先执行这个闭包函数删除自己然后执行挂在once下的订阅者fn,用完就删除,因为如果要用remove方法删除once订阅者,和删除普通订阅者是不一样的,仅仅通过传入fn(fns[i]===fn)不能删除once订阅者(因为传入的fn函数和oncewrapper函数引用不相等),需要使用wrapper函数下的fn属性引用(fns.fn===fn)标识要删除的订阅者。EventEmitter完整源码:https://github.com/booms21/Ev...以上总结就是发布订阅模型从实现到具体讲解的全部内容。不管做什么,都需要一些套路。设计模式是前人总结出来的编程套路,学习这些套路才是进阶的必由之路。有了套路,不仅避免了开发中的一些问题,也省去了很多浪费在想造轮子上的时间,是事半功倍的。
