发布-订阅模式又叫做观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
该模式可以用于登录时,将用户信息发布给各个订阅了的模块。如导航栏,购物车,消息队列,收获地址管理等
实现全局定义,具有命名空间,先发布后订阅的发布-订阅模式
//创建全局事件车对象
var Event = (function(){
var global = this, //调用该立即执行函数的对象
Event, //返回的事件车,封装各种函数的实现
_default = 'default'; //默认的命名空间名
Event = function(){
var _listen, //订阅
_trigger, //发布
_remove, //移除订阅
_slice = Array.prototype.slice,
_shift = Array.prototype.shift,
_unshift = Array.prototype.unshift,
namespaceCache = {}, //命名空间缓存
_create, //创建命名空间
each = function( ary, fn ){
var ret;
for( var i = 0, l = ary.length; i < l; i++){
var n = ary[i];
ret = fn.call( n, i, n);
}
return ret;
};
_listen = function( key, fn, cache ){
//缓存订阅函数
if( !cache[ key ] ){
cache[ key ] = [];
}
cache[key].push( fn );
};
_remove = function( key, cache, fn){
//判断订阅事件是否存在
if( cahce[ key ] ){
//判断是否提供了订阅者
if( fn ){
// 因为中途需要删除数组元素,所以采用倒叙,防止访问溢出
for( var i = cache[ key ].length; i >= 0; i--){
if( cache[ key ][i] === fn ){
//订阅者存在,删除订阅者
cache[ key ].splice( i, 1);
}
}
}else{
//未提供订阅者,则事件删除所有订阅者
cache[ key ] = [];
}
}
};
_trigger = function(){
var cache = _shift.call(arguments), //取出命名空间中的全部事件缓存
key = _shift.call(arguments), //取出订阅事件
args = arguments, // 取出发布的消息
_self = this, //此命名空间
stack = cache[ key ]; //取出key事件的订阅者队列
if( !stack || !stack.length ){
return;
}
//迭代执行订阅者
return each( stack, function(){
//因为each中fn.call(n, i, n); 所以this指向订阅者
return this.apply( _self, args );
});
};
//创建命名空间
_create = function( namespace ){
var namespace = namespace || _deault;
var cache = {}, //缓存订阅者
offlineStack = [], //离线事件,当无订阅者时,发布的事件缓存
ret = {
//当个命名空间对象中的方法
listen: function( key, fn, last ){
_listen( key, fn, cache); //将fn推入此此命名空间的订阅者缓存
if ( offlineStack === null ){
return; //无离线事件,结束
}
if( last === 'last' ){
offlineStack.length && offlineStack.pop()();//订阅事件只有一个,推出并执行
}else{
each( offlineStack, function(){
this();
});
}
offlineStack = null;
},
//移除key事件的所有订阅,且fn只接收一次
one: function( key, fn, last ){
_remove( key, cache );
this.listen( key, fn, last );
},
remove: function( key, fn ){
_remove( key, cache, fn);
},
trigger: function(){
var fn,
args,
_self = this; //指向此命名空间
_unshif.call( arguments, cache ); //添加此命名空间的事件缓存
args = arguments;
fn = function(){
return _trigger.apply( _self, args );
};
//如果离线栈存在,说明没有订阅者
if( offlineStack ){
return offlineStack.push( fn );
};
return fn();
}
};
//_create函数的return
return namespace ?
//命名空间存在,则返回,否则创建后返回
( namespaceCache[ namespace ] ? namespaceCache[ namespace ] :
namespaceCache[ namespace ] = ret )
: ret;
};
//内部Event函数的return
return {
create: _create,
//以下为未指定命名空间,则使用默认命名空间
one: function( key, fn, last ){
var event = this.create();
event.one( key,fn,last );
},
remove: function( key,fn ){
var event = this.create();
event.remove( key,fn );
},
listen: function( key, fn, last ){
var event = this.create();
event.listen( key, fn, last );
},
trigger: function(){
var event = this.create();
event.trigger.apply(this,arguments);
}
};
}();
return Event;
})();
使用
/************** 先发布后订阅 *************/
Event.trigger('click',1);
Event.listen('click',function( a ){
console.log(a);
});
/*************** 使用命名空间 ***********/
Event.create( 'namespace1' ).listen('click',function(a){
console.log(a); //1
});
Event.create('namespace1').trigger('click',1);
Event.create( 'namespace2' ).listen('click',function(a){
console.log(a); //2
});
Event.create('namespace2').trigger('click',2);