JavaScript编程设计模式之观察者模式(Observer Pattern)实例详解


Posted in Javascript onOctober 25, 2017

本文实例讲述了JavaScript编程设计模式之观察者模式。分享给大家供大家参考,具体如下:

简介

简单的解释观察者模式,就是一个对象(subject)维护一个依赖他的对象(observers)列表,当自身状态发生变化时,自动通知所有观察者对象。当某个对象不需要获得通知时,可以从对象列表中删除掉。

从上面的解释中我们可以提炼出三个componet: Subject, ObserverList和Observer,用JS实现很简单:

function ObserverList(){
 this.observerList = [];
}
ObserverList.prototype.Add = function( obj ){
 return this.observerList.push( obj );
};
ObserverList.prototype.Empty = function(){
 this.observerList = [];
};
ObserverList.prototype.Count = function(){
 return this.observerList.length;
};
ObserverList.prototype.Get = function( index ){
 if( index > -1 && index < this.observerList.length ){
  return this.observerList[ index ];
 }
};
ObserverList.prototype.Insert = function( obj, index ){
 var pointer = -1;
 if( index === 0 ){
  this.observerList.unshift( obj );
  pointer = index;
 }else if( index === this.observerList.length ){
  this.observerList.push( obj );
  pointer = index;
 }
 return pointer;
};
ObserverList.prototype.IndexOf = function( obj, startIndex ){
 var i = startIndex, pointer = -1;
 while( i < this.observerList.length ){
  if( this.observerList[i] === obj ){
   pointer = i;
  }
  i++;
 }
 return pointer;
};
ObserverList.prototype.RemoveAt = function( index ){
 if( index === 0 ){
  this.observerList.shift();
 }else if( index === this.observerList.length -1 ){
  this.observerList.pop();
 }
};
// Extend an object with an extension
function extend( extension, obj ){
 for ( var key in extension ){
  obj[key] = extension[key];
 }
}

Subject拥有增加和删除Observer的能力

function Subject(){
 this.observers = new ObserverList();
}
Subject.prototype.AddObserver = function( observer ){
 this.observers.Add( observer );
};
Subject.prototype.RemoveObserver = function( observer ){
 this.observers.RemoveAt( this.observers.IndexOf( observer, 0 ) );
};
Subject.prototype.Notify = function( context ){
 var observerCount = this.observers.Count();
 for(var i=0; i < observerCount; i++){
  this.observers.Get(i).Update( context );
 }
};

最后定义一个观察者对象,实现update方法

// The Observer
function Observer(){
 this.Update = function(){
  // ...
 };
}

当有多个观察者,只需扩展上面的基本对象,并重写Update方法。

尽管观察则模式被广泛使用,但在JS中经常使用它的变体: 发布订阅模式

发布订阅模式通过一个topic/event通道,解耦了观察者模式中Subject(发布者)和Observer(订阅者)之间耦合的问题,在JS中被广泛使用。

下面简单的例子说明了使用发布订阅模式的基本结构

// A very simple new mail handler
// A count of the number of messages received
var mailCounter = 0;
// Initialize subscribers that will listen out for a topic
// with the name "inbox/newMessage".
// Render a preview of new messages
var subscriber1 = subscribe( "inbox/newMessage", function( topic, data ) {
 // Log the topic for debugging purposes
 console.log( "A new message was received: ", topic );
 // Use the data that was passed from our subject
 // to display a message preview to the user
 $( ".messageSender" ).html( data.sender );
 $( ".messagePreview" ).html( data.body );
});
// Here's another subscriber using the same data to perform
// a different task.
// Update the counter displaying the number of new
// messages received via the publisher
var subscriber2 = subscribe( "inbox/newMessage", function( topic, data ) {
 $('.newMessageCounter').html( mailCounter++ );
});
publish( "inbox/newMessage", [{
 sender:"hello@google.com",
 body: "Hey there! How are you doing today?"
}]);
// We could then at a later point unsubscribe our subscribers
// from receiving any new topic notifications as follows:
// unsubscribe( subscriber1, );
// unsubscribe( subscriber2 );

发布订阅模式的实现

许多Js库都很好的实现了发布订阅模式,例如Jquery的自定义事件功能。

// Publish
// jQuery: $(obj).trigger("channel", [arg1, arg2, arg3]);
$( el ).trigger( "/login", [{username:"test", userData:"test"}] );
// Dojo: dojo.publish("channel", [arg1, arg2, arg3] );
dojo.publish( "/login", [{username:"test", userData:"test"}] );
// YUI: el.publish("channel", [arg1, arg2, arg3]);
el.publish( "/login", {username:"test", userData:"test"} );
// Subscribe
// jQuery: $(obj).on( "channel", [data], fn );
$( el ).on( "/login", function( event ){...} );
// Dojo: dojo.subscribe( "channel", fn);
var handle = dojo.subscribe( "/login", function(data){..} );
// YUI: el.on("channel", handler);
el.on( "/login", function( data ){...} );
// Unsubscribe
// jQuery: $(obj).off( "channel" );
$( el ).off( "/login" );
// Dojo: dojo.unsubscribe( handle );
dojo.unsubscribe( handle );
// YUI: el.detach("channel");
el.detach( "/login" );

简单实现

var pubsub = {};
(function(q) {
  var topics = {},
    subUid = -1;
  // Publish or broadcast events of interest
  // with a specific topic name and arguments
  // such as the data to pass along
  q.publish = function( topic, args ) {
    if ( !topics[topic] ) {
      return false;
    }
    var subscribers = topics[topic],
      len = subscribers ? subscribers.length : 0;
    while (len--) {
      subscribers[len].func( topic, args );
    }
    return this;
  };
  // Subscribe to events of interest
  // with a specific topic name and a
  // callback function, to be executed
  // when the topic/event is observed
  q.subscribe = function( topic, func ) {
    if (!topics[topic]) {
      topics[topic] = [];
    }
    var token = ( ++subUid ).toString();
    topics[topic].push({
      token: token,
      func: func
    });
    return token;
  };
  // Unsubscribe from a specific
  // topic, based on a tokenized reference
  // to the subscription
  q.unsubscribe = function( token ) {
    for ( var m in topics ) {
      if ( topics[m] ) {
        for ( var i = 0, j = topics[m].length; i < j; i++ ) {
          if ( topics[m][i].token === token) {
            topics[m].splice( i, 1 );
            return token;
          }
        }
      }
    }
    return this;
  };
}( pubsub ));

使用方法

// Another simple message handler
// A simple message logger that logs any topics and data received through our
// subscriber
var messageLogger = function ( topics, data ) {
  console.log( "Logging: " + topics + ": " + data );
};
// Subscribers listen for topics they have subscribed to and
// invoke a callback function (e.g messageLogger) once a new
// notification is broadcast on that topic
var subscription = pubsub.subscribe( "inbox/newMessage", messageLogger );
// Publishers are in charge of publishing topics or notifications of
// interest to the application. e.g:
pubsub.publish( "inbox/newMessage", "hello world!" );
// or
pubsub.publish( "inbox/newMessage", ["test", "a", "b", "c"] );
// or
pubsub.publish( "inbox/newMessage", {
 sender: "hello@google.com",
 body: "Hey again!"
});
// We cab also unsubscribe if we no longer wish for our subscribers
// to be notified
// pubsub.unsubscribe( subscription );
// Once unsubscribed, this for example won't result in our
// messageLogger being executed as the subscriber is
// no longer listening
pubsub.publish( "inbox/newMessage", "Hello! are you still there?" );

更多关于JavaScript相关内容可查看本站专题:《javascript面向对象入门教程》、《JavaScript切换特效与技巧总结》、《JavaScript查找算法技巧总结》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
js 与或运算符 || &amp;&amp; 妙用
Dec 09 Javascript
基于jquery的超简单上下翻
Apr 20 Javascript
jquery选择器、属性设置用法经验总结
Sep 08 Javascript
JavaScript数据类型详解
Apr 01 Javascript
JS实现仿QQ聊天窗口抖动特效
May 10 Javascript
JavaScript中的toDateString()方法使用详解
Jun 12 Javascript
jQuery插件实现带圆点的焦点图片轮播切换
Jan 18 Javascript
javascript弹出窗口中增加确定取消按钮
Jun 24 Javascript
详解使用Node.js 将txt文件转为Excel文件
Jul 05 Javascript
JS从非数组对象转数组的方法小结
Mar 26 Javascript
angular6.0开发教程之如何安装angular6.0框架
Jun 29 Javascript
JSON获取属性值方法代码实例
Jun 30 Javascript
BootStrap 标题设置跨行无效的解决方法
Oct 25 #Javascript
vue基于mint-ui的城市选择3级联动的示例
Oct 25 #Javascript
浅谈Vue的加载顺序探讨
Oct 25 #Javascript
JavaScript模块模式实例详解
Oct 25 #Javascript
vue生成token保存在客户端localStorage中的方法
Oct 25 #Javascript
AngularJS实现的获取焦点及失去焦点时的表单验证功能示例
Oct 25 #Javascript
浅析为什么a=&quot;abc&quot; 不等于 a=new String(&quot;abc&quot;)
Oct 25 #Javascript
You might like
更改localhost为其他名字的方法
2014/02/10 PHP
PHP的一个完美GIF等比缩放类,附带去除缩放黑背景
2014/04/01 PHP
PHP获取指定函数定义在哪个文件中以及其所在的行号实例
2014/05/08 PHP
php检查日期函数checkdate用法实例
2015/03/19 PHP
php源码分析之DZX1.5随机数函数random用法
2015/06/17 PHP
ThinkPHP框架表单验证操作方法
2017/07/19 PHP
实现PHP中session存储及删除变量
2018/10/15 PHP
PHP实现字符串的全排列详解
2019/04/24 PHP
javascript(js)的小数点乘法除法问题详解
2014/03/07 Javascript
jquery利用命名空间移除绑定事件的方法
2015/03/11 Javascript
Bootstrap项目实战之子栏目资讯内容
2016/04/25 Javascript
关于input全选反选恶心的异常情况
2016/07/24 Javascript
JavaScript学习笔记之惰性函数示例详解
2017/08/27 Javascript
JS+canvas动态绘制饼图的方法示例
2017/09/12 Javascript
webpack学习教程之前端性能优化总结
2017/12/05 Javascript
JavaScript使用indexOf()实现数组去重的方法分析
2018/09/04 Javascript
angular6 填坑之sdk的方法
2018/12/27 Javascript
从vue源码看props的用法
2019/01/09 Javascript
JS实现电脑虚拟键盘的操作
2020/06/24 Javascript
小程序自定义弹框效果
2020/11/16 Javascript
vue 插槽简介及使用示例
2020/11/19 Vue.js
Python实现多并发访问网站功能示例
2017/06/19 Python
解决pycharm 误删掉项目文件的处理方法
2018/10/22 Python
Python图像处理之图像的读取、显示与保存操作【测试可用】
2019/01/04 Python
python爬取cnvd漏洞库信息的实例
2019/02/14 Python
Python如何读取文件中图片格式
2020/01/13 Python
Python基于tkinter canvas实现图片裁剪功能
2020/11/05 Python
美国面料纺织品商城:Fabric.com
2017/06/28 全球购物
世界上获奖最多的手机镜头:Olloclip
2018/03/03 全球购物
用JAVA实现一种排序,JAVA类实现序列化的方法(二种)
2014/04/23 面试题
技术学校毕业生求职信分享
2013/12/02 职场文书
优秀纪检干部材料
2014/08/27 职场文书
工伤事故处理协议书怎么写
2014/10/15 职场文书
2014年保育员个人工作总结
2014/12/02 职场文书
给校长的建议书范文
2015/09/14 职场文书
2019年大学生职业生涯规划书
2019/03/25 职场文书