从一道面试题简单谈谈发布订阅和观察者模式_第1页
从一道面试题简单谈谈发布订阅和观察者模式_第2页
从一道面试题简单谈谈发布订阅和观察者模式_第3页
从一道面试题简单谈谈发布订阅和观察者模式_第4页
从一道面试题简单谈谈发布订阅和观察者模式_第5页
已阅读5页,还剩1页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

从⼀道⾯试题简单谈谈发布订阅和观察者模式今天的话题是javascript中常被提及的「发布订阅模式和观察者模式」,提到这,我不由得想起了⼀次⾯试。记得在去年的⼀次求职⾯试过程中,⾯试官问我,“你在项⽬中是怎么处理⾮⽗⼦组件之间的通信的?”。我答道,“有⽤到vuex,有的场景也会⽤EventEmitter2”。⾯试官继续问,“那你能⼿写代码,实现⼀个简单的EventEmitter吗?”⼿写EventEmitter我犹豫了⼀会⼉,想到使⽤EventEmitter2时,主要是⽤emit发事件,⽤on监听事件,还有off销毁事件监听者,removeAllListeners销毁指定事件的所有监听者,还有once之类的⽅法。考虑到时间关系,我想着就先实现发事件,监听事件,移除监听者这⼏个功能。当时可能有点紧张,不过有惊⽆险,在⾯试官给了⼀点提⽰后,顺利地写出来了!现在把这部分代码也记下来。classEventEmitter{constructor(){//维护事件及监听者this.listeners={}}/***注册事件监听者*@param{String}type事件类型*@param{Function}cb回调函数*/on(type,cb){if(!this.listeners[type]){this.listeners[type]=[]}this.listeners[type].push(cb)}/***发布事件*@param{String}type事件类型*@param{...any}args参数列表,把emit传递的参数赋给回调函数*/emit(type,...args){if(this.listeners[type]){this.listeners[type].forEach(cb=>{cb(...args)})}}/***移除某个事件的⼀个监听者*@param{String}type事件类型*@param{Function}cb回调函数*/off(type,cb){if(this.listeners[type]){consttargetIndex=this.listeners[type].findIndex(item=>item===cb)if(targetIndex!==-1){this.listeners[type].splice(targetIndex,1)}if(this.listeners[type].length===0){deletethis.listeners[type]}}}/***移除某个事件的所有监听者*@param{String}type事件类型*/offAll(type){if(this.listeners[type]){deletethis.listeners[type]}}}}//创建事件管理器实例constee=newEventEmitter()//注册⼀个chifan事件监听者ee.on('chifan',function(){console.log('吃饭了,我们⾛!')})//发布事件chifanee.emit('chifan')//emit传递参数ee.on('chifan',function(address,food){console.log(`吃饭了,我们去${address}吃${food}!`)})此时会打印两条信息,因为前⾯注册了两个chifan事件的监听者也可以ee.emit('chifan','三⾷堂','铁板饭')////测试移除事件监听consttoBeRemovedListener=function(){console.log('我是⼀个可以被移除的监听者')}ee.on('testoff',toBeRemovedListener)ee.emit('testoff')ee.off('testoff',toBeRemovedListener)ee.emit('testoff')//此时事件监听已经被移除,不会再有console.log打印出来了//chifan的所有事件监听测试移除ee.offAll('chifan')console.log(ee)//ee.listeners已经变成空对象了,再emit发送chifan事件也不会有反应了此时可以看到有了这个⾃⼰写的简单版本的EventEmitter,我们就不⽤依赖第三⽅库啦。对了,vue也可以帮我们做这样的事情。constee=newVue();ee.$on('chifan',function(address,food){console.log(`吃饭了,我们去${address}吃${food}!`)})ee.$emit('chifan','三⾷堂','铁板饭')所以我们可以单独new⼀个Vue的实例,作为事件管理器导出给外部使⽤。想测试的朋友可以直接打开vue官⽹,在控制台试试,也可以在⾃⼰的vue项⽬中实践下哦。发布订阅模式其实仔细看看,EventEmitter就是⼀个典型的发布订阅模式,实现了事件调度中⼼。发布订阅模式中,包含发布者,事件调度中⼼,订阅者三个⾓⾊。我们刚刚实现的EventEmitter的⼀个实例ee就是⼀个事件调度中⼼,发布者和订阅者是松散耦合的,互不关⼼对⽅是否存在,他们关注的是事件本⾝。发布者借⽤事件调度中⼼提供的emit⽅法发布事件,⽽订阅者则通过on进⾏订阅。如果还不是很清楚的话,我们把代码换下单词,是不是变得容易理解⼀点呢?classPubSub{constructor(){//维护事件及订阅⾏为this.events={}}/***注册事件订阅⾏为*@param{String}type事件类型*@param{Function}cb回调函数*/subscribe(type,cb){if(!this.events[type]){this.events[type]=[]}this.events[type].push(cb)}/***发布事件*@param{String}type事件类型*@param{...any}args参数列表*/publish(type,...args){if(this.events[type]){this.events[type].forEach(cb=>{cb(...args)})}}/***移除某个事件的⼀个订阅⾏为*@param{String}type事件类型*@param{Function}cb回调函数*/unsubscribe(type,cb){if(this.events[type]){consttargetIndex=this.events[type].findIndex(item=>item===cb)if(targetIndex!==-1){this.events[type].splice(targetIndex,1)}if(this.events[type].length===0){deletethis.events[type]}}}/***移除某个事件的所有订阅⾏为*@param{String}type事件类型*/unsubscribeAll(type){if(this.events[type]){deletethis.events[type]}}}画图分析最后,我们画个图加深下理解:特点发布订阅模式中,对于发布者Publisher和订阅者Subscriber没有特殊的约束,他们好似是匿名活动,借助事件调度中⼼提供的接⼝发布和订阅事件,互不了解对⽅是谁。松散耦合,灵活度⾼,常⽤作事件总线易理解,可类⽐于DOM事件中的dispatchEvent和addEventListener。缺点当事件类型越来越多时,难以维护,需要考虑事件命名的规范,也要防范数据流混乱。观察者模式观察者模式与发布订阅模式相⽐,耦合度更⾼,通常⽤来实现⼀些响应式的效果。在观察者模式中,只有两个主体,分别是⽬标对象Subject,观察者Observer。观察者需Observer要实现update⽅法,供⽬标对象调⽤。update⽅法中可以执⾏⾃定义的业务代码。⽬标对象Subject也通常被叫做被观察者或主题,它的职能很单⼀,可以理解为,它只管理⼀种事件。Subject需要维护⾃⾝的观察者数组observerList,当⾃⾝发⽣变化时,通过调⽤⾃⾝的notify⽅法,依次通知每⼀个观察者执⾏update⽅法。按照这种定义,我们可以实现⼀个简单版本的观察者模式。//观察者classObserver{/***构造器*@param{Function}cb回调函数,收到⽬标对象通知时执⾏*/constructor(cb){if(typeofcb==='function'){this.cb=cb}else{thrownewError('Observer构造器必须传⼊函数类型!')}}/***被⽬标对象通知时执⾏*/update(){this.cb()}}//⽬标对象classSubject{constructor(){//维护观察者列表this.observerList=[]}/***添加⼀个观察者*@param{Observer}observerObserver实例*/addObserver(observer){this.observerList.push(observer)}/***通知所有的观察者*/notify(){this.observerList.forEach(observer=>{observer.update()})}}constobserverCallback=function(){console.log('我被通知了')}constobserver=newObserver(observerCallback)constsubject=newSubject();subject.addObserver(observer);subject.notify();画图分析最后也整张图理解下观察者模式:特点⾓⾊很明确,没有事件调度中⼼作为中间者,⽬标对象Subject和观察者Observer都要实现约定的成员⽅法。双⽅联系更紧密,⽬标对象的主动性很强,⾃⼰收集和维护观察

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论