zoukankan      html  css  js  c++  java
  • javascript观察者模式

          观察者模式又叫发布-订阅模式,它定义对象间的一对多的依赖关系,当一个对象的状态发生该变时,所有依赖于它的对象都将得到通知。在JavaScript中,一般用事件模型来替代传统的观察者模式。
         下面是售楼处(发布者)与各看房者(订阅者)的例子:
         var event = {


              clientList:[], //缓存列表


              listen:function(key,fn){ //增加订阅者
                      if(!this.clientList[key]){
                             this.clientList[key] = [];
                      }
                      this.clientList[key].push(fn); //订阅的消息添加进缓存列表
              },


              trigger:function(){ //发布消息
                        var key = Array.prototype.shift.call(arguments),
                             fns = this.clientList[key];
                        if(!fns || fns.length == 0){ //没有绑定对应的消息
                                return false;
                        }
                        for(var i=0,fn; fn=fns[i++]){
                                fn.apply(this, arguments);
                        }
                },


               remove:function(key,fn){ //删除订阅
                          var fns = this.clientList[key];
                          if(!fns){ //如果key对应的消息没有被人订阅,则直接返回
                                return false;
                          }
                          if(!fn){ //如果没有传入具体的回调函数,表示需要取消key对应消息的所有订阅
                               fns && (fns.length = 0);
                          }else{
                               for(var l=fns.length-1; l>=0; l--){ //反向遍历订阅的回调函数列表
                                    var _fn = fns[l];
                                    if(_fn ===fn){
                                          fns.splice(l,1); //删除订阅者的回调函数
                                    }
                               }
                           }
                }
         };

         var installEvent = function(obj){ //给所有对象动态安装发布-订阅功能
              for(var i in event){
                   obj[i] = event[i];
              }
          };

          var salesOffices = {}; //定义售楼处
          installEvent(salesOffices);

          salesOffices.listen('squareMeter100',function(price){ // 张三订阅消息
                console.log('价格=' + price);
          });

          salesOffices.listen('squareMeter150',function(price){ // 李四订阅消息
                console.log('价格=' + price);
          });

          salesOffices.trigger('squareMeter100',2000000); // 输出 2000000
          salesOffices.trigger('squareMeter150',3000000); // 输出 3000000


          上面的代码还存在两个小问题:
               1.每个发布者对象都添加了listen和trigger方法,以及一个缓存列表clientList,这是一种资源浪费
               2.订阅者跟售楼处对象存在一定的耦合性,订阅者至少要知道售楼处对象的名字是salesOffices,才能订阅到事件

          下面是对以上两个问题的改良:

          var event = {
               var clientList:[],
               listen,
               trigger,
               remove;

               listen = function(key,fn){
                    if(!clientList[key]){
                         clientList[key] = [];
                    }
                   clientList[key].push(fn); //订阅的消息添加进缓存列表
                };

               trigger = function(){ //发布消息
                    var key = Array.prototype.shift.call(arguments),
                    fns = clientList[key];
                    if(!fns || fns.length == 0){ //没有绑定对应的消息
                        return false;
                    }
                    for(var i=0,fn; fn=fns[i++]){
                         fn.apply(this, arguments);
                    }
               };

               remove = function(key,fn){ //删除订阅
                       var fns = clientList[key];
                       if(!fns){ //如果key对应的消息没有被人订阅,则直接返回
                            return false;
                       }
                       if(!fn){ //如果没有传入具体的回调函数,表示需要取消key对应消息的所有订阅
                           fns && (fns.length = 0);
                       }else{
                           for(var l=fns.length-1; l>=0; l--){ //反向遍历订阅的回调函数列表
                                var _fn = fns[l];
                                if(_fn ===fn){
                                       fns.splice(l,1); //删除订阅者的回调函数
                                }
                           }
                      }
                 };

                return {
                       listen:listen,
                       trigger:trigger,
                       remove:remove
                };
         };

         Event.listen('squareMeter150',function(price){ // 李四订阅消息
                  console.log('价格=' + price);
         });

          Event.trigger('squareMeter150',2000000); // 输出 2000000


          改良后,发布-订阅模式可以用一个全局的Event对象来实现,订阅者不需要了解消息来自哪个发布者,发布者也不需要了解消息会推送给哪些订阅者,Event作为类似“中介者”的角色,把订阅者和发布者联系起来。

          观察者模式的优点非常明显,一为时间上的解耦,二为对象间的解耦。它的应用非常广泛,既可以用在异步编程中,也可以用来编写更松耦合的代码编写。但也不是没有缺点。创建订阅者本身要消耗一定的时间和内存,而且当你订阅一个消息后,也许此消息始终都没发生,但这个订阅者会始终存在于内存中。另外,观察者模式虽然可以弱化对象间的联系,但如果过度使用的话,对象间的必要联系也将被深藏在背后,会导致程序难以跟踪维护和理解。特别是有多个发布者和订阅者嵌套到一起的时候,要跟踪一个bug不是键轻松的事。

  • 相关阅读:
    软件测试需求分析与跟踪
    应用程序通用测试技术
    测试计算机软件发展历史
    多个iframe的刷新问题
    web:div模块自适应问题
    求:多人合作代码管理的好办法
    第一天
    jQuery,选择器,选择父页面的元素
    鼠标移入移出冒泡事件解决 Jquery mouseenter和mouseleave
    jQuery DOM操作IE6兼容性
  • 原文地址:https://www.cnblogs.com/xbj-2016/p/5818586.html
Copyright © 2011-2022 走看看