再起航,我的学习笔记之JavaScript设计模式18(观察者模式)
观察者模式
观察者模式(Observer):
又被称为发布-订阅者模式或消息机制,定义了一种依赖关系,解决了主体对象与观察者之间功能的耦合。
创建一个观察者对象
首先我们创建一个闭包对象,让其在页面加载就立即执行
var Observer=(function(){
//为了防止消息队列暴露而被篡改,我们将消息容器设置为静态私有变量
var _messages={};
return{
//注册信息接口
regist:function(){},
//发布信息接口
fire:function(){},
//移除信息接口
remove:function(){}
}
})();
现在我们观察者对象的雏形创建出来了,接着我们就要一一实现这三个方法,我们首先实现消息注册方法
注册方法的作用是将订阅者注册的消息退路到消息队列中,因此我们需要接受两个参数:消息类型以及相应的处理动作,在退路到详细队列时如果此消息不存在则应该创建一个该消息类型并将消息放入消息队列中,如果此消息存在则应该将消息执行方法推入该消息对应的执行方法队列中,这么做的目的也是保证多个模块注册同一则消息时能顺利执行。
//注册信息接口
regist:function(type,fn){
//如果此消息不存在我们创建一个该消息类型
if(typeof _messages[type]==='undefined'){
//将动作推入到该消息对应的动作执行队列中
_messages[type]=[fn];
}else{
//如果此消息存在,我们将动作方法推入该消息对应的动作执行序列中
_message[type].push(fn);
}
}
接下来我们实现发布信息的方法对于发布信息的方法,其功能是当观察者发布一个消息时将所有订阅者订阅的消息一次执行。所以应该接收两个参数,消息类型以及动作执行时需要传递的参数,当然在这里消息类型是必须的。在执行消息动作队列之前校检信息的存在是很有必要的,然后遍历消息执行方法队列并依次执行。然后将消息类比一级传递的参数打包后一次传入消息执行方法中
//发布信息接口
fire:function(type,args){
//如果该消息没被注册则返回
if(!_messages[type]) return;
//定义消息信息
var events={
type:type, //消息类型
args:args||{} //消息携带数据
},
i=0,
len=_messages[type].length;
//遍历消息动作
for(var i=0;i<_messages[type].length;i++){
//一次执行注册的消息对应的动作序列
_messages[type][i]call(this,events);
}
}
最后我们来实现移除消息方法,移除消息方法,其功能是将订阅者注销的消息从消息队列中清楚,因此我们也需要两个参数,即消息类型以及执行的某一动作。当然在删除消息时我们也需要校检消息是否存在
//移除消息接口
remove:function(type,fn){
//如果消息动作队列对象存在
if(_messages[type] instanceof Array){
//从最后一个消息动作遍历
for(var i=_messages[type].length-1;i>=0;i--){
//如果存在该动作真在消息动作序列中移除相应动作
_messages[type][i]===fn&&_messages[type].splice(i,1);
}
}
}
好了现在我们的观察者对象创建成功了,具体结构如下
var Observer=(function(){
//为了防止消息队列暴露而被篡改,我们将消息容器设置为静态私有变量
var _messages={};
return{
//注册信息接口
regist:function(type,fn){
//如果此消息不存在我们创建一个该消息类型
if(typeof _messages[type]==='undefined'){
//将动作推入到该消息对应的动作执行队列中
_messages[type]=[fn];
}else{
//如果此消息存在,我们将动作方法推入该消息对应的动作执行序列中
_messages[type].push(fn);
}
},
//发布信息接口
fire:function(type,args){
//如果该消息没被注册则返回
if(!_messages[type]) return;
//定义消息信息
var events={
type:type, //消息类型
args:args||{} //消息携带数据
},
i=0,
len=_messages[type].length;
//遍历消息动作
for(var i=0;i<_messages[type].length;i++){
//一次执行注册的消息对应的动作序列
_messages[type][i]call(this,events);
}
},
//移除信息接口
remove:function(type,fn){
//如果消息动作队列对象存在
if(_messages[type] instanceof Array){
//从最后一个消息动作遍历
for(var i=_messages[type].length-1;i>=0;i--){
//如果存在该动作真在消息动作序列中移除相应动作
_messages[type][i]===fn&&_messages[type].splice(i,1);
}
}
}
}
})();
我们来测试一下,首先我们订阅一条信息
Observer.regist('test',function(e){
console.log(e.type,e.args.msg);
});
接着我们发布这条消息
Observer.fire('test',{msg:'传递参数'});
我们来看一看结果

接着我们在用一个具体的例子来展示观察者对象的作用。
我们首先创建公司类,公司是被提需求的对象,因此当它们是订阅者。同时公司也有对需求分析,以及沟通需求的动作
//公司类
var company=function(demand){
var that=this;
//这是一个需求
that.demand=demand;
//沟通需求动作
that.communication=function(){
console.log(that.demand);
}
}
当然我们也需要对需求分析,我们再添加一个分析方法
company.prototype.analysis=function(demand){
//我们注册一下
Observer.regist(demand,this.communication);
}
当然还有需求沟通失败的情况,谈崩了那就没得谈了,所以我们再加一个失败的方法
company.prototype.breakdown=function(demand){
console.log(this.demand+''+demand+'谈判失败')
//取消对需求的监听
Observer.remove(demand,this.communication);
}
既然有了公司类,那么我们还需要创建一个客户类,他会像公司提出需求,所以它有提出需求的动作
//客户类
var client=function(){};
//客户提需求的方法
client.prototype.ask=function(demand){
console.log("需求是:"+demand);
//发布需求消息
Observer.fire(demand);
}
现在我们公司类和客户类都创建完成了,接下来我们创建3家需要参与竞标的公司
var company1=new company("第一家公司沟通需求");
var company2=new company("第二家公司沟通需求");
var company3=new company("第三家公司沟通需求");
然后我们分别获取客户需求
company1.analysis("我需要一个门户网站");
company2.analysis("我需要一款CRM软件");
company3.analysis("我需要一个商城项目");
company3.analysis("我需要一个门户网站");
然后我们第三家公司门户网站项目沟通失败了
company3.breakdown("我需要一个门户网站");
接着我们创建一个客户类
var client=new client();
客户提出了三个需求
client.ask("我需要一个门户网站");
client.ask("我需要一款CRM软件");
client.ask("我需要一个商城项目");
好了我们调用来看一下

总结
观察者模式最主要的作用是解决类或对象之间的耦合,解耦两个相互依赖的对象,使其依赖于观察者的消息机制这样对于任意一个订阅者对象来说,其他订阅者对象的改变不会影响到自身。对于每一个订阅者来说,其自身既可以是消息的发出者也可以是消息的执行者,这都依赖于调用观察者对象的三种方法(订阅消息,注销消息,发布消息)中的某一种。
在实际应用中观察者模式适用于团队开发,当要完成一个涉及多模块调用的需求,观察者模式的优势就显而易见了,模块间的信息传递不必要相互引用其他模块,只需要通过观察者模式注册或者发布消息即可。
也谢谢大家看到这里:)如果你觉得我的分享还可以请点击推荐,分享给你的朋友让我们一起进步~
好了以上就是本次分享的全部内容,本次示例参考自JavaScript设计模式一书,让我们一点点积累一点点成长,希望对大家有所帮助。
欢迎转载,转载请注明作者,原文出处。
再起航,我的学习笔记之JavaScript设计模式18(观察者模式)的更多相关文章
- 再起航,我的学习笔记之JavaScript设计模式02
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 我们 ...
- 再起航,我的学习笔记之JavaScript设计模式01
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 在通 ...
- 再起航,我的学习笔记之JavaScript设计模式03
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 上一 ...
- 再起航,我的学习笔记之JavaScript设计模式04
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 上回 ...
- 再起航,我的学习笔记之JavaScript设计模式05(简单工程模式)
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...
- 再起航,我的学习笔记之JavaScript设计模式06(工厂方法模式)
上一次已经给大家介绍了简单工厂模式,相信大家对创建型设计模式有了初步的了解,本次我将给大家介绍的是工厂方法模式. 工厂方法模式 工厂方法模式(Factory Method):通过对产品类的抽象使其创建 ...
- 再起航,我的学习笔记之JavaScript设计模式05(简单工厂模式)
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...
- 再起航,我的学习笔记之JavaScript设计模式06(抽象工厂模式)
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前两 ...
- 再起航,我的学习笔记之JavaScript设计模式08(建造者模式)
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...
随机推荐
- Java中的clone()----深复制,浅复制
这篇文章主要介绍了Java中对象的深复制(深克隆)和浅复制(浅克隆) ,需要的朋友可以参考下 1.浅复制与深复制概念 ⑴浅复制(浅克隆) 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他 ...
- Angular页面选项卡切换要注意的toggleClass
在第一,第二篇随笔中写到的选项卡切换,我回头看了看发现缺少了一个很重要的部分,那就是toggleClass. //在js控制器中必须写明指令,要不然在页面写了toggle-class="ac ...
- Git的一些知识
Git Git的特点: Git存储的是文件快照, 即整个文件内容, 并保存指向快照的索引 分布式 原理 这个之前面试实习的时候被问到过, 搞懂基本原理还是很重要的 Git的目录结构在执行git ini ...
- hdu 6093---Rikka with Number(计数)
题目链接 Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, s ...
- table表格中单击添加动态编辑框
var $newNode=$("<input type='text' style='width:250px; height:20px; maxlength='20' id='texti ...
- 数据库db2错误代码大全
sqlcode sqlstate 说明000 00000 SQL语句成功完成01xxx SQL语句成功完成,但是有警告+012 01545 未限定的列名被解释为一个有相互关系的引用+098 01568 ...
- vcruntime140.dll丢失解决
之前安装好apache2.4后,打开突然报这样的错误,所以百度一下,发现了问题所在,原因是系统需要安装一个插件,才能正常运行apache2.4 Microsoft Visual C++ 2005 Re ...
- TensorFlow学习笔记2——数据类型及简单运算
0. 小试牛刀 首先,激活tensorflow环境( source activate tensorflow ),随后在ipython里: import tensorflow as tf sess = ...
- js实现轮播图动画
在网页浏览中,可以看到轮播图是无处不在的,这是一个前端工程最基本的技巧.首先看看几个网页的呈现的效果. QQ音乐: 网易云音乐: 天猫: 接下来将从简到难总结几种实现轮播图的方法. 1.样式一:鼠标滑 ...
- 【NO.11】Jmeter - 构建1个可供Linux使用的Jmeter测试脚本 - 共3个步骤
在Linux使用Jmeter做性能测试需要4个前提条件,这4个前提条件已经在之前的文档里提到了,重复一下加深印象: (1) 在本地已安装xshell 参考<SecureCRT-转换密钥-Xshe ...