当前位置: 首页 > 后端技术 > Node.js

一个JS事件管理模块

时间:2023-04-03 12:15:38 Node.js

关于事件我们在使用javascript开发的时候,经常会用到很多事件,比如点击,键盘,鼠标等等,这些物理事件。而我们今天要说的,我称之为事件,是事件的另一种形式,订阅---发布,也就是所谓的观察者模式,它定义了一种一对多的依赖关系,当一个对象状态发生变化时,所有依赖于它的对象都会收到通知,而在javascript中,一般习惯使用事件模型来代替发布-订阅模型。举一个生活中的例子来帮助你理解这种模式。炎炎夏日,妈妈把米饭煮好放在桌子上,热气腾腾。这时候,妈妈叫小明吃饭(小明在隔壁房间饿了,晚上就去吃鸡。。。),小明出来一看,跟妈妈说,等一会一边‘饭凉了’再叫我,太热了……十分钟后……妈妈叫你‘饭凉了’,快来吃饭,这时候小明听到妈妈在喊‘饭菜凉了,赶紧出来吃完。这个例子就是上面介绍的订阅-发布模式。例子中,小明是订阅者(订阅'饭凉了'),妈妈是发布者(发布信号'饭凉了')。使用订阅---发布模式有明显的优势:订阅者不用一直问发布者饭凉了。你可以过来吃饭。这样一来,就没有必要强行将小明和他妈妈联结在一起了。小明的弟弟妹妹,饭菜凉了想吃,只要跟妈妈说一声就可以了。就像每个观察者一定接触过的一种订阅---发布:DOM事件的绑定document.body.addEventListener('click',function(e){console.log('我执行了...')},false)回到主题:*通过订阅-发布模式实现event-mange*一步步实现event-mange模块的主要方法:on:subscriber,addeventemit:publisher,starteventonce:subscribe或者,添加一个只能监听一次然后失效的事件removeListener:删除单个订阅(事件)removeAllListener:删除单个事件类型订阅或删除所有订阅getListenerCount:获取订阅者数量的主要属性event-mange模块:MaxEventListNum:设置单个事件的最大订阅数(默认为10)基本框架首先我们希望通过event.on,event.emit进行订阅和发布,通过构造函数创建一个事件实例,on和emit是这个实例方法的两个实例,同样上面列出的所有主要方法都是事件对象的原型方法。functionevents(){};//枚举我们要实现的事件对象的方法event.prototype.on=function(){};event.prototype.emit=function(){};event.prototype.once=功能(){};event.prototype.removeListener=function(){};event.prototype.removeAllListener=function(){};event.prototype.getListenerCount=function(){};好像少了点什么,是的,就是我们上面列出的事件对象的MaxEventListNum属性,我们会加上functionevent(){//因为MaxEventListNum属性是开发者可以设置的//所以在没有设置的时候,我们将其设置为取消定义。MaxEventListNum=this.MaxEventListNum||不明确的;//如果不设置set的话,我们不能让监听的个数无限大//这样可能会造成内存溢出//所以我们设置默认个数为10(当然,设置为其他数字也是可以的)this.defaultMaxEventListNum=10;}至此,基本上我们要实现的时间管理模块的属性和方法的初始状态就差不多了,也就是说骨架出来后,我们需要填写它的代码逻辑,让他成为有血有肉的人(好像有生命似的。。。)值得思考的是,我们搭建好骨架后,要做的就是一个订阅-发布的模型,那么多的订阅事件应该怎么记住呢??首先,对于一个订阅,我们需要有一个订阅类型,也就是一个主题。对于这个主题,我们需要把所有订阅这个主题的事件放在一起。是的,你可以选择Array。初始构建event_list:{topic1:[fn1,fn2,fn3...]...}然后我们将存放我们事件的event_list放入代码中完善,作为事件函数event()的属性{//这里我们做一个简单的判断,避免一些意想不到的错误if(!this.event_list){this.event_list={};}this.MaxEventListNum=this.MaxEventListNum||不明确的;this.defaultMaxEventListNum=10;}on方法实现event.prototype.on=function(){};通过分析,on方法应该首先接收一个订阅的topic,然后是当topic响应时触发的回调方法event.prototype.on=function(eventName,content){};eventName作为事件type,作为event_list的一个属性,将所有事件类型为eventName的监听器压入eventName数组event.prototype.on=function(eventName,content){...var_event,ctx;_event=this.event_list;//再次判断event_list是否存在,不存在则重新赋值if(!_event){_event=this.事件列表={};}else{//获取当前监听的eventNamectx=this.event_list[eventName];}//判断是否有这个监听类型//如果不存在,说明这个事件是第一次监听//直接赋值回调函数内容if(!ctx){ctx=this.event_list[活动名称]=内容;//改变订阅者数量ctx.ListenerCount=1;}elseif(isFunction(ctx)){//判断这个属性是否是一个函数(如果是一个函数,说明已经有且只有一个订阅者)//将eventName类型从函数改为数组ctx=this.event_list[事件名称]=[ctx,内容];//此时订阅者数量发生变化ctx.ListenerCount=ctx.length;}elseif(isArray(ctx)){//判断是否是数组,如果是数组,直接pushctx.push(content);ctx.ListenerCount=ctx.length;}...};once方法实现了event.prototype.once=function(){};once方法只对订阅的事件执行一次,执行后立即删除event_list中对应订阅类型属性中的订阅回调函数,其存储过程与on方法几乎相同,同样需要一个订阅类型的topic和响应事件的回调contentevent.prototype.once=function(eventName,content){};执行完这个事件回调后立即取消注册这个订阅,而如果此时为同一类事件注册了多个监听回调,我们是无法准确删除当前once方法注册的监听回调的,所以通常我们使用遍历事件监听队列中找到对应的监听回调然后删除是不可行的。幸运的是,伟大的javascript语言为我们提供了强大的闭包特性,通过闭包来装饰内容并将其包装成一个全新的函数。events.prototype.once=function(event,content){...//once和on有相同的存储事件回调机制//dealOnce函数包装函数this.on(event,dealOnce(this,event,content));...}//封装函数functiondealOnce(target,type,content){varflag=false;//通过闭包特性(函数的外部引用会保存在scope中)functionpackageFun(){//当这个monitor回调被调用时,会先删除回调方法。this.removeListener(type,packageFun);如果(!flag){flag=true;//因为关闭了,所以还是会保留原来的监听回调,所以内容会被执行。申请(目标,参数);}packageFun.content=内容;}返回包乐趣;}once的实现其实是封装了两次我们自己传过来的回调函数,然后绑定封装的函数,封装的函数先执行removeListener()解除回调函数和事件的绑定,再执行回调函数emit方法实现event.prototype.emit=function(){};emit方法用于发布事件并驱动相应事件监听队列的执行事件中的监听回调,所以我们需要一个事件类型topicovent.prototype.emit=function(eventName[,message][,message1][,...]){};当然事件发布了,也可以像这个事件的监听器传递参数,数量不限,依次传递给所有的监听器回调event.prototype.emit=function(eventName[,message]){var_event,ctx;//除第一个参数eventNmae外,其他参数保存存在于数组中varargs=Array.prototype.slice.call(arguments,1);_event=this.event_list;//检测存储事件队列是否存在if(_event){//如果存在,则获取此监听类型ctx=this.event_list[eventName];}//检测该监听类型的事件队列//如果不存在则直接返回if(!ctx){returnfalse;}elseif(isFunction(ctx)){//如果是红薯,直接执行,并将所有参数传递给这个函数(回调函数)ctx.apply(this,args);}elseif(isArray(ctx)){//如果是数组,遍历调用for(vari=0;i(this.MaxEventListNum?this.MaxEventListNum:this.defaultMaxEventListNum)){//当超过最大限制时,会发出警告ctx.maxed=true;console.warn('events.MaxEventListNum||[MaxEventListNum]:订阅数量超过最大值,不设置默认为10');}else{ctx.maxed=false;}}}...现在Vue可以调用了红紫没关系,events-manage也可以在Vue中全局使用events.prototype.install=function(Vue,Option){Vue.prototype.$ev=这个;}不用多解释了,想必读者都明白如何使用(在Vue中)这个库更具体详细的文档,这里不好戳码字。如果您觉得对您有帮助,请给个大大的赞好吗?哈哈(……大清早了……)