标签:
观察者模式(有时又被称为发布-订阅Subscribe>模式、模型-视图View>模式、源-收听者Listener>模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。(来源于百度百科)
还是不懂,没关系
今天晚上老大要上线一个活动,这时候PM来通知你了,
“XX,今天有个活动需要你支持一下,巴拉巴拉,上线了发个邮件给我,我先回家了。”
“尼玛,F**K!”
但是没办法,这个事件已经跟你绑定了,只有完成条件才能解绑,于是你疯狂code,终于上完了线,发了邮件通报,这个时候这个事件才算完成。
这个栗子里面,PM就是一个订阅者(bind),向你订阅了一个邮件通报,他可以不用关心你的code过程,只需要获得通知即可。你就是一个发布者,在满足上线这个要求后,才触发了发邮件的操作(trigger)。
百度百科里面有个单词大家不造有没有注意到,事件处理,而javascript
就是纯事件驱动的语言。为什么说事件驱动适合用观察者模式解决,分析一下事件产生的要素你就知道,一个事件必须包含事件时间、事件对象、事件起因、事件经过、事件结果。
什么!你要把这些全跟我说!
对于一个日理万机的攻城狮来说,你TM只要告诉我事件结果就好了,我只需要知道这个事情已经发生即可。
PM:在每个播放的链接上加个调起逻辑
ok,so easy,最常见的实现使用事件委托
function openAPP(event) {
event.preventDefault();
//open app
......
}
$(‘body‘).on(‘click‘, ‘[data-openapp=true]‘, openAPP);
然而事情没有那么简单:
PM:现在登录的用户直接播放,未登录的用户弹登录框
Coder:OK!
如果你的登录接口是个异步请求,你可能会这样做:
function login() {
$.ajax({
url: ‘/login‘,
success: function(data) {
if(data.login === true) {
window.login = true;
}
}
});
}
$(‘body‘).on(‘click‘, ‘[data-openapp=true]‘, function(event) {
event.preventDefault();
if(typeof window.login !== ‘undefined‘
&& window.login === true) {
location.href = $(this).data(‘openapp-url‘);
}else {
//show dialog
......
}
}
你以为事情完了吗?NO
PM:已登录的用户不用展示首屏广告了,另外送个五块钱券吧!
Coder:WTF!
没办法,改吧。
function showADs() {
if(typeof window.login !== ‘undefined‘
&& window.login === true) {
return;
}else {
//show ads
......
}
}
//送券部分省略
然而以上代码只适用于模板登录情况,如果登录接口是异步接口呢,那只能去login
回调函数里面加一个去除广告的逻辑了。事情做到这,你有没有感觉到一丝丝egg hurt,如果PM再加这样的逻辑肿么办?有没有办法做到登录之后就发出通知呢,所有的跟登录相关的事件都绑定到这个通知上去?
仔细分析浏览器事件绑定的原理,我们可以发现,案列一中那个最常见的dom事件绑定
代码,其实内部实现原理是这样子的,浏览器为每个节点维护一个eventMap,类似于
eventMap = {
‘click‘ : [
fn1,
fn2
],
‘touchstart‘ : [
fn3,
fn4
]
}
当浏览器USER Interface
线程捕获到用户的click
操作时,浏览器就去eventMap里面查找索引为click
数据,发现里面有fn1
、fn2
,并依次执行。
在上面的代码中,我们只是向浏览器订阅了一个click
事件,事件在什么时候、什么时间触发全都由浏览器内部实现。在发生click
的时候你可以执行fn1
、fn2
、….fnn
。这个不就是我们所需要解决的事件耦合的问题嘛!事情到这,就很简单了,我们只需要实现一个自定义事件队列
即可。
参考code,来自alloyteam
Events = function() {
var listen, log, obj, one, remove, trigger, __this;
obj = {};
__this = this;
listen = function( key, eventfn ) {
var stack, _ref;
stack = ( _ref = obj[key] ) != null ? _ref : obj[ key ] = [];
return stack.push( eventfn );
};
one = function( key, eventfn ) {
remove( key );
return listen( key, eventfn );
};
remove = function( key ) {
var _ref;
return ( _ref = obj[key] ) != null ? _ref.length = 0 : void 0;
};
trigger = function() {
var fn, 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) {
return false;
}
}
return {
listen: listen,
one: one,
remove: remove,
trigger: trigger
}
}
现在再来改我们之前的代码
var eventCenter = new Events();
eventCenter.listen(‘login‘, function(data) {
//隐藏广告
hideADs(arguments);
});
eventCenter.listen(‘login‘, function(data) {
//登录播放跳转
openUrl(arguments);
});
eventCenter.listen(‘login‘, function(data) {
//赠送代金券
sendCash(arguments);
});
function login() {
$.ajax({
url: ‘/login‘,
success: function(data) {
if(data.login === true) {
eventCenter.trigger(‘login‘);
}
}
});
}
完成!
因为观察者模式应用之广,本来想发散出去讲的,浏览器中的观察者模式、前后端数据交互中的观察者模式,发现涉及的内容太多,根本停不下来,所以先大致对观察者模式做个简单介绍,后续想到再写,让大家了解观察者模式的两个特性,为解耦而生、为事件而生。下一篇博客里我打算讲讲当前前端比较火的backbone
、angularjs
以及一些MVP
框架中的观察者模式的应用。
因为之前一段时间比较忙以及很久都没写过博客,前端公众号虽然筹备了很久但是一直没去写,相信万事开头难,千里之行始于足下。如有建议或者意见,欢迎反馈。
标签:
原文地址:http://www.cnblogs.com/leavenup/p/4581678.html