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

发布-订阅模式又叫做观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

该模式可以用于登录时,将用户信息发布给各个订阅了的模块。如导航栏,购物车,消息队列,收获地址管理等

实现全局定义,具有命名空间,先发布后订阅的发布-订阅模式

//创建全局事件车对象
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);

你可能感兴趣的:(JavaScript观察者模式(发布-订阅模式))