轻松掌握:JavaScript观察者模式

观察者模式

观察者模式也叫“订阅者/发布者”模式,定义对象间的一种一对多的依赖关系,发布者可以向所有订阅者发布消息。

观察者模式被广泛地应用于JavaScript客户端编程中。所有的浏览器事件(mouseover,keypress等)都是使用观察者模式的例子。

使用这个模式的最主要目的就是促进对象之间的解耦(弱化对象之间的联系)。在观察者模式中,一组对象订阅另一个对象的指定活动并得到通知。

如:

document.body.addEventListener('click',function(){
    alert(2);
},false);

以上代码订阅了document.body的click事件,当body被点击时,body便会向订阅者发布消息;

实现观察者模式的一般步骤:

  1. 先指定谁充当发布者;
  2. 给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者;
  3. 发布消息时,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数;还可在回调函数内加入一些参数;
    (也可先发布消息,将消息保存起来,当有订阅者时就将该消息重新发布给他)
发布-订阅模式的通用实现:
var Publisher = (function(){
    //listenerList用来保存一系列的key,每个key为保存了所有订阅者函数fn的数组{ keyA:[fn1,fn2...], keyB:[...]...}
    //类似:可以为同一事件注册多个回调函数
    var listenerList = {};
    return {
        addListener: function(key,fn){
            if(!listenerList[key]){
                listenerList[key] = [];
            }
            if(listenerList[key].indexOf(fn) === -1){ //检测fn是否已订阅,注意indexOf方法不支持IE9以下,可用for循环代替
                listenerList[key].push(fn);
            }
        },
        publish: function(){
            var key = Array.prototype.shift.call(arguments),
                fns = listenerList[key];
            if(!fns||fns.length === 0){
                return false;
            }
            for(var i = 0,fn;fn = fns[i++];){
                fn.apply(this,arguments);
            }
        },
        removeListener: function(key,fn){
            var fns = listenerList[key];
            if(!fns){
                return false;
            }
            if(!fn){ //若没指定fn,则表示取消所有订阅
                fns && (fns.length = 0);
            }else{  //可直接用fns.splice(listenerList[key].indexOf(fn),1);但indexOf方法不支持IE9以下
                for(var l = fns.length - 1; l >=  0; l--){
                    if(fns[l] === fn){
                        fns.splice(l,1);
                    }
                }
            }
        }
    };
})();

Publisher.addListener('month',function(num){  //订阅month
    console.log('本月数量: '+num);
});
Publisher.addListener('month',function(price){
    console.log('本月价格: '+price);
});
Publisher.addListener('week',function(num){
    console.log('本周数量: '+num);
});
Publisher.publish('month',123);  //手动发布消息
//本月数量: 123
//本月价格: 123
Publisher.publish('week',5);
//本周数量: 5

实际应用:

$.ajax('http://xxx.com?login',function(data){
   Publisher.publish('loginSuccess',data);  //若登录成功则向所有订阅者发布登录成功的消息
});
//以下在各个模块添加订阅消息
var header = (function () {
    Publisher.addListener('loginSuccess', function (data) {
        header.setAvatar(data.avatar);
    });
    return {
        setAvatar: function (data) {
            console.log('设置header模块的头像')
        }
    }
})();
var nav = (function () {
    Publisher.addListener('loginSuccess', function (data) {
        nav.setAvatar(data.avatar);
    });
    return {
        setAvatar: function (avatar) {
            console.log('设置header模块的头像')
        }
    }
})();
//...可随意添加其他模块

参考文献:
《JavaScript模式》
《JavaScript设计模式与开发实践》

原文地址:https://www.cnblogs.com/susufufu/p/5808885.html