javascript设计模式之观察者模式
栏目: JavaScript · 发布时间: 5年前
内容简介:这篇笔记主要记录学习思路及收获,分享出来抛砖引玉,如有谬误或优化空间,欢迎交流。要理解观察者模式,可以类比vue中的基于上面的思路,首先要有一个对象,它有一个私有的列表属性和对外暴露的两个方法
这篇笔记主要记录学习思路及收获,分享出来抛砖引玉,如有谬误或优化空间,欢迎交流。
要理解观察者模式,可以类比vue中的 EventBus
,其实就是一个全局的观察者对象( $bus
),上面有注册事件( $bus.on()
)和发送事件( $bus.emit()
)的方法,当然因为需要会注册很多事件,所以内部还有一个事件列表属性 _events
来存储注册的事件。下面为学习笔记,对观察者模式做简单实现。
基于上面的思路,首先要有一个对象,它有一个私有的列表属性和对外暴露的两个方法
let Observer = (()=>{ _events:[]; return { retister:()=>{}, issue:()=>{}, } })()
接下来一步步实现。
首先,观察者对象内部要有一个存储事件的列表属性
let Observer = (()=>{ // 防止事件队列暴露而被篡改故将事件容器作为静态私有变量保存 let _events = []; })()
其次是注册事件的 register
方法,需要两个参数:需要观察的事件名称 type
和这个事件被触发后具体的执行内容 fn
:
let Observer = (()=>{ // 防止事件队列暴露而被篡改故将事件容器作为静态私有变量保存 let _events = {}; return { register: (type,fn)=>{ //如果此消息类型不存在则应该创建一个(判断对象上是否有某个属性) // if (typeof(_events[type])==='undefined'){ if (!(type in _events)){ _events[type] = [fn]; } else {//如果此消息类型已存在,则直接将对应动作推入消息队列 _events[type].push(fn); } }, } })()
再然后就是触发事件的 issue
方法,也需要两个参数:要触发的事件 type
及传递过去的参数 arg
let Observer = (()=>{ // 防止事件队列暴露而被篡改故将事件容器作为静态私有变量保存 let _events = {}; return { register: (type,fn)=>{ //如果此消息类型不存在则应该创建一个(判断对象上是否有某个属性) // if (typeof(_events[type])==='undefined'){ if (!(type in _events)){ _events[type] = [fn]; } else {//如果此消息类型已存在,则直接将对应动作推入消息队列 _events[type].push(fn); } }, issue: (type,arg)=>{ // 如果没有此类型,返回 // if (typeof (_events[type]) === 'undefined'){ if (!(type in _events)) { return; } for (let i=0;i<_events[type].length;i++){ _events[type][i](arg); } }, } })()
最后,可以再加一个可以移除已监听事件的 remove
方法
let Observer = (()=>{ // 防止事件队列暴露而被篡改故将事件容器作为静态私有变量保存 let _events = {}; return { register: (type,fn)=>{ //如果此消息类型不存在则应该创建一个(判断对象上是否有某个属性) // if (typeof(_events[type])==='undefined'){ if (!(type in _events)){ _events[type] = [fn]; } else {//如果此消息类型已存在,则直接将对应动作推入消息队列 _events[type].push(fn); } }, issue: (type,arg)=>{ // 如果没有此类型,返回 // if (typeof (_events[type]) === 'undefined'){ if (!(type in _events)) { return; } for (let i=0;i<_events[type].length;i++){ _events[type][i](arg); } }, // 此方法可以类比document.removerEventListener(evt,fn) remove: (type,fn)=>{ // 如果这个类型的事件存在 if (_events[type] instanceof Array){ for (let i = 0; i < _events[type].length; i++) { _events[type][i] === fn && _events[type].splice(i,1); } } }, // 因为外部获取不到私有属性`_events`,所以临时用这个方法检查`remove`方法是否生效 check (){ console.log('_events事件队列',_events); } } })()
至此一个基本的观察者对象就写出来了,接下来跑一下看效果。
// 注册的事件被触发后需要执行的动作 function handler (val){ console.log('val:',val*2); } // 注册事件及对应的执行动作 Observer.register('eventName',handler); // 触发事件 function test (){ Observer.issue('eventName',5); //10 // 对比执行remove事件前后的事件列表内容 Observer.check(); Observer.remove('eventName',fn); observer.check(); }
观察者模式在解决类的耦合中的应用小例子。想象一下课堂上老师提问,学生回答问题的场景。老师提问的问题名称相当于发出的事件,如果学生听到这个问题(事先注册了这个问题名称),那么便会回答(响应事件的动作)。
学生类:
let Student = function (result) { // 问题答案 this.result = result; // 回答的动作 this.answer = ()=>{ console.log('答案',this.result); } } Student.prototype.listen = function(question){ Observer.register(question,this.answer); } Student.prototype.sleep = function (question) { Observer.remove(question,this.answer); console.log('这个问题我听不到听不到~'); }
教师类:
let Teacher = function (){} // 对某一个问题发出问题 Teacher.prototype.ask = function (question) { Observer.issue(question); }
执行:
let student1 = new Student('学生1回答问题'); let student2 = new Student('学生2回答问题'); let student3 = new Student('学生3:呼噜呼噜~'); student1.listen('鸡生蛋还是蛋生鸡?'); student2.listen('鸡生蛋还是蛋生鸡?'); student3.listen('鸡生蛋还是蛋生鸡?'); let teacher = new Teacher(); // 老师发问 function test () { teacher.ask('鸡生蛋还是蛋生鸡?'); } //答案 学生1回答问题 //es5观察者.html:68 答案 学生2回答问题 //es5观察者.html:68 答案 学生3:呼噜呼噜~
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
你的品牌,价值千万
温迪 / 人民邮电出版社 / 2018-7-1 / 49.00元
“大道无术,万法归心。” 不管是互联网、社交媒体,还是 AI 怎样让人眼花缭乱。从“真心”出发塑造的个人品牌,都将带你从容面对任何一种变化的冲击。现代生活变得越来越透明,如果你不懂得如何真实、精准地定位和呈现自己,你的个人品牌在 碎片信息中被误解、被曲解就是一种必然。 本书分四步引导你剖析自己、发现自我,构建可持续的品牌生态系统,策划品牌战略,提升个人呈现力,并在最后带你勾画出一幅完整的个人......一起来看看 《你的品牌,价值千万》 这本书的介绍吧!