zl程序教程

您现在的位置是:首页 >  前端

当前栏目

JavaScript设计模式之观察者模式(发布者-订阅者模式)

JavaScript模式设计模式 订阅 观察者
2023-06-13 09:15:46 时间

观察者模式(又叫发布者-订阅者模式)应该是最常用的模式之一.在很多语言里都得到大量应用.包括我们平时接触的dom事件.也是js和dom之间实现的一种观察者模式.

复制代码代码如下:


div.onclick = functionclick(){
alert(”click")
}

只要订阅了div的click事件.当点击div的时候,functionclick就会被触发。

那么到底什么是观察者模式呢.先看看生活中的观察者模式。

好莱坞有句名言.“不要给我打电话,我会给你打电话”.这句话就解释了一个观察者模式的来龙去脉。其中“我”是发布者,“你”是订阅者。

再举个例子,我来公司面试的时候,完事之后每个面试官都会对我说:“请留下你的联系方式,有消息我们会通知你”。在这里“我”是订阅者,面试官是发布者。所以我不用每天或者每小时都去询问面试结果,通讯的主动权掌握在了面试官手上。而我只需要提供一个联系方式。

观察者模式可以很好的实现2个模块之间的解耦。假如我正在一个团队里开发一个html5游戏.当游戏开始的时候,需要加载一些图片素材。加载好这些图片之后开始才执行游戏逻辑.假设这是一个需要多人合作的项目.我完成了Gamer和Map模块,而我的同事A写了一个图片加载器loadImage。

loadImage的代码如下:

复制代码代码如下:

loadImage( imgAry, function(){
Map.init();
Gamer.init();
})

当图片加载好之后,再渲染地图,执行游戏逻辑.嗯,这个程序运行良好.突然有一天,我想起应该给游戏加上声音功能.我应该让图片加载器添上一行代码.
复制代码代码如下:
loadImage( imgAry, function(){
Map.init();
Gamer.init();
Sount.init();
})

可是写这个模块的同事A去了外地旅游.于是我打电话给他,喂.你的loadImage函数在哪,我能不能改一下,改了之后有没有副作用.如你所想,各种不淡定的事发生了.如果当初我们能这样写呢:
复制代码代码如下:
loadImage.listen(”ready",function(){
Map.init();
})
loadImage.listen(”ready",function(){
Gamer.init();
})
loadImage.listen(”ready",function(){
Sount.init();
})

loadImage完成之后,它根本不关心将来会发生什么,因为它的工作已经完成了.接下来它只要发布一个信号.

复制代码代码如下:
loadImage.trigger(”ready");

那么监听了loadImage的"ready"事件的对象都会收到通知.就像上个面试的例子.面试官根本不关心面试者们收到面试结果后会去哪吃饭.他只负责把面试者的简历搜集到一起.当面试结果出来时照着简历上的电话挨个通知.

说了这么多概念,来一个具体的实现.实现过程其实很简单.面试者把简历扔到一个盒子里,然后面试官在合适的时机拿着盒子里的简历挨个打电话通知结果.

复制代码代码如下:
Events=function(){
varlisten,log,obj,one,remove,trigger,__this;
obj={};
__this=this;
listen=function(key,eventfn){ //把简历扔盒子,key就是联系方式.
varstack,_ref; //stack是盒子
stack=(_ref=obj[key])!=null?_ref:obj[key]=[];
returnstack.push(eventfn);
};
one=function(key,eventfn){
remove(key);
returnlisten(key,eventfn);
};
remove=function(key){
var_ref;
return(_ref=obj[key])!=null?_ref.length=0:void0;
};
trigger=function(){ //面试官打电话通知面试者
varfn,stack,_i,_len,_ref,key;
key=Array.prototype.shift.call(arguments);
stack=(_ref=obj[key])!=null?_ref:obj[key]=[];
for(_i=0,_len=stack.length;_i<_len;_i++){
fn=stack[_i];
if(fn.apply(__this, arguments)===false){
returnfalse;
}
}
return{
listen:listen,
one:one,
remove:remove,
trigger:trigger
}
}

最后用观察者模式来做一个成人电视台的小应用.

复制代码代码如下:
//订阅者
varadultTv=Event();
adultTv.listen( ”play", function(data){
alert(“今天是谁的电影”+data.name);
});
//发布者
adultTv.trigger( ”play", {‘name":‘麻生希"} )