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 相关文章推荐
jquery实现简单的拖拽效果实例兼容所有主流浏览器
Jun 21 Javascript
js中onload与onunload的使用示例
Aug 25 Javascript
javascript获取xml节点的最大值(实现代码)
Dec 11 Javascript
js实现图片拖动改变顺序附图
May 13 Javascript
Node.js实现的简易网页抓取功能示例
Dec 05 Javascript
Javascript设计模式之观察者模式的多个实现版本实例
Mar 03 Javascript
javascript实现起伏的水波背景效果
May 16 Javascript
BootStrap实现手机端轮播图左右滑动事件
Oct 13 Javascript
JS库之Waypoints的用法详解
Sep 13 Javascript
p5.js码绘“跳动的小正方形”的实现代码
Oct 22 Javascript
Vue 解决通过this.$refs来获取DOM或者组件报错问题
Jul 28 Javascript
解决Vue中使用keepAlive不缓存问题
Aug 04 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
PHP中对数据库操作的封装
2006/10/09 PHP
解析php通过cookies获取远程网页的指定代码
2013/06/25 PHP
php的闭包(Closure)匿名函数初探
2016/02/14 PHP
php结合ajax实现手机发红包的案例
2016/10/13 PHP
php常用字符串String函数实例总结【转换,替换,计算,截取,加密】
2016/12/07 PHP
在IIS下安装PHP扩展的方法(超简单)
2017/04/10 PHP
PHP+redis实现微博的推模型案例分析
2019/07/10 PHP
JavaScript用Number方法实现string转int
2014/05/13 Javascript
一个Action如何调用两个不同的方法
2014/05/22 Javascript
JavaScript数据类型之基本类型和引用类型的值
2015/04/01 Javascript
js实现的早期滑动门菜单效果代码
2015/08/27 Javascript
JAVASCRIPT代码编写俄罗斯方块网页版
2015/11/26 Javascript
javascript实现的网站访问量统计代码
2015/12/20 Javascript
微信小程序收藏功能的实现代码
2018/06/12 Javascript
微信小程序提取公用函数到util.js及使用方法示例
2019/01/10 Javascript
JavaScript 判断iPhone X Series机型的方法
2019/01/28 Javascript
用Electron写个带界面的nodejs爬虫的实现方法
2019/01/29 NodeJs
vue使用map代替Aarry数组循环遍历的方法
2020/04/30 Javascript
vue3.0中setup使用(两种用法)
2020/12/02 Vue.js
关于uniApp editor微信滑动问题
2021/01/15 Javascript
[01:22:10]Ti4 循环赛第二日 DK vs Empire
2014/07/11 DOTA
Python中还原JavaScript的escape函数编码后字符串的方法
2014/08/22 Python
Python中关于字符串对象的一些基础知识
2015/04/08 Python
python opencv设置摄像头分辨率以及各个参数的方法
2018/04/02 Python
详解Numpy数组转置的三种方法T、transpose、swapaxes
2019/05/27 Python
Python OpenCV调用摄像头检测人脸并截图
2020/08/20 Python
python基于pdfminer库提取pdf文字代码实例
2019/08/15 Python
使用Python函数进行模块化的实现
2019/11/15 Python
商得四方公司面试题(gid+)
2014/04/30 面试题
厂长岗位职责
2014/02/19 职场文书
奥利奥广告词
2014/03/20 职场文书
春节请假条
2014/04/11 职场文书
党的生日活动方案
2014/08/15 职场文书
关于运动会的广播稿
2014/09/22 职场文书
优秀班主任工作总结2015
2015/05/25 职场文书
2016大学生诚信考试承诺书
2016/03/25 职场文书