视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
javascript设计模式之观察者模式详细介绍
2020-11-27 20:23:47 责编:小采
文档


Javascript活跃在事件驱动的环境中,比如鼠标的响应、事件的回调、网络的请求等,观察者模式又称发布者-订阅者(publisher-subscriber)模式,是处理对象及其行为和状态之间的关系,管理人与任务之间的关系。

1. 最常见的观察者模式

1.1 事件
document.body.addEventListener('click', function () {
 console.log('you clicked me, poor guy!')
});

这是最简单最普通的一种观察者模式,除此click 以外还有loadblurdragfocusmouseover、等。事件(listener)有别于事件处理器(handler),在事件中,一个事件可以关联多个,每个处理监听到的消息;事件处理器是执行处理事件发生后的关联函数,一种事件是能有一个处理函数:

var dom = $('.dom');
var listener1 = function(e){
 //do one thing
}
var listener2 = function(e){
 //do another thing
}
addEvent(dom,'click',listener1);
addEvent(dom,'click',listener2);

在这个事件的例子中,listener1listener2 都是dom元素的,当dom被点击时,都会执行各自的函数;

var dom = document.getElementById('dom');
var handler1 = function(e){
 //do one thing
}
var handler2 = function(e){
 //do another thing
}
dom.onclick = handler1;
dom.onclick = handler2;

在这个事件处理器的例子中,handler1不会被执行,只执行handler2,是一次赋值的操作。

1.2 动画

在动画中广泛使用了观察者模式,动画的开始、完成、暂停等,都需要观察者来确定物体的行为和状态。

//定义动画
var Animation = function(){
 this.onStart = new Publisher; //关于Publisher的设计将在1.3节介绍
 this.onComplete = new Publisher;
 this.onTween = new Publisher;
}
//定义一个原型方法
Animation.prototype.look = function(){
 this.onStart.deliver('animation started!');
 this.onTween.deliver('animation is going on!');
 this.onComplete.deliver('animation completed!'); 
};

//实例一个box对象
var box = new Animation();

//定义三个函数作为subscribers
var openBox = function(msg){
 console.log(msg)
}
var checkBox = function(msg){
 console.log(msg)
}
var closeBox = function(msg){
 console.log(msg)
}

//订阅事件
openBox.subscribe(box.onStart);
checkBox.subscribe(box.onTween);
closeBox.subscribe(box.onComplete);

//调用方法
box.look()

//animation started!
//animation is going on!
//animation completed!

1.3 观察者的构建

首先,需要一个发布者。先定义一个构造函数,为其定义一个数组,用以保存订阅者信息:

function Publisher(){
 this.subscribes = [];
}

发布者具有发布消息的功能,定义一个deliver的原型函数:

Publisher.prototype.deliver = function(data){
 this.subscribes.forEach(function(fn){
 fn(data);
 });
 return this;
}

接下来构造订阅方法:

Function.prototype.subscribe = function(publisher){
 var that = this;
 var alreadyExists = publisher.subscribes.some(function(el){
 return el === that;
 });
 if(!alreadyExists){
 publisher.subscribes.push(this);
 }
 return this;
}

直接在Function的prototype添加subscribe方法,这样所有函数都可以调用该方法。这样就构建完毕了,使用方法参看1.2动画的用例。
比较直观的解释(以onStart为例):当box对象执行look方法时,执行onStart.deliver(),将onStart事件发布出去,广播通知'animation started!',这个时候,一直在监听onStartopenBox监听到该事件发布的信息,打印出来。

1.4 另一种构建观察者的方式

这种方式模仿了nodejs的事件处理机制,代码也比较简洁:

 var scope = (function() {
 //消息列表
 var events = {};
 return {
 //订阅消息
 on:function(name,hander){
 var index = 0; //记录消息时间的索引
 if(events[name]){ 
 //消息名已存在,将处理函数放到该消息的事件队列中
 index = events[name].push(hander) - 1; 
 }else{
 events[name] = [hander];
 }
 //返回当前消息处理事件的移除函数
 return function(){
 events[name].splice(index,1);
 }
 },
 //关闭消息
 off:function(name){
 if(!events[name]) return;
 //消息存在,删除消息
 delete events[name];
 },
 //消息发布
 emit:function(name,msg){
 //消息不存在,不处理
 if(!events[name]) return;
 //消息存在,将该事件处理队列中每一个函数都执行一次
 events[name].forEach(function(v,i){
 v(msg);
 });
 }
 }
})();

var sayHello = scope.on('greeting',function(msg){
 console.log('订阅消息:' + msg);
});

var greeting = function(msg){
 console.log('发布消息:' + msg);
 scope.emit('greeting', msg);
}

greeting('hello Panfen!')

1.5 nodejs中观察者模式的实现方案

nodejs中有events模块来实现观察者模式,可参考Nodejs API-Events 谈观察者模式,大多数的模块都集成了events模块,所以可以直接使用emit发射事件和on监听事件,或者像下面这样先定义一下;

var EventEmitter = require('events').EventEmitter;
var life = new EventEmitter();
life.setMaxListeners(11); //设置最大监听数,默认10

//发布和订阅sendName
life.on('sendName',function(name){
 console.log('say hello to '+name);
});
life.emit('sendName','jeff');

//发布和订阅sendName2
function sayBeautiful(name){
 console.log(name + ' is beautiful');
}
life.on('sendName2',sayBeautiful);
life.emit('sendName2','jeff');

常用方法:

  • hasConfortListener :用于判断发射的事件是否有

  • removeListener :移除

  • listenerCount :该事件所有的总数

  • removeAllListeners :移除事件所有(或某个)的

  • 1.6 总结

    观察者模式建立了推送收听的逻辑,适用于希望把人的行为和应用程序的行为分开的场合。举个例子来说:用户点击导航栏的一个tab时,会打开包含更多选项的子菜单,一般会选择在知道哪个元素的情况下直接监听这个click事件,这样做的弊端在于实现了与click事件直接绑在一起。更好的做法是:创建一个可观察的onTabChange对象,关联若干观察者实现。

    相关文章:

    详解JavaScript设计模式经典之策略模式

    JavaScript设计模式经典之简单工厂模式代码实例

    JavaScript设计模式经典之单例模式详解

    下载本文
    显示全文
    专题