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 使用手册(二)
Sep 23 Javascript
jQuery页面滚动浮动层智能定位实例代码
Aug 23 Javascript
js设置文本框中焦点位置在最后的示例代码(简单实用)
Mar 04 Javascript
在css加载完毕后自动判断页面是否加入css或js文件
Sep 10 Javascript
JavaScript数组和循环详解
Apr 27 Javascript
javascript自定义右键弹出菜单实现方法
May 25 Javascript
TypeScript 中接口详解
Jun 19 Javascript
创建一个类Person的简单实例
May 17 Javascript
原生JS实现自定义滚动条效果
Oct 27 Javascript
对vux点击事件的优化详解
Aug 28 Javascript
vue侧边栏动态生成下级菜单的方法
Sep 07 Javascript
Javascript作用域和作用域链原理解析
Mar 03 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 5.3.1 安装包 VC9 VC6不同版本的区别是什么
2010/07/04 PHP
PHP使用POP3读取邮箱接收邮件的示例代码
2020/07/08 PHP
监控 url fragment变化的js代码
2010/04/19 Javascript
理解JAVASCRIPT中hasOwnProperty()的作用
2013/06/05 Javascript
JS实现支持多选的遍历下拉列表代码
2015/08/20 Javascript
微信小程序 rpx 尺寸单位详细介绍
2016/10/13 Javascript
Canvas + JavaScript 制作图片粒子效果
2017/02/08 Javascript
webpack配置的最佳实践分享
2017/04/21 Javascript
详解利用jsx写vue组件的方法示例
2017/07/17 Javascript
js禁止浏览器页面后退功能的实例(推荐)
2017/09/01 Javascript
原生js实现公告滚动效果
2021/01/10 Javascript
实例分析编写vue组件方法
2019/02/12 Javascript
js中的面向对象之对象常见创建方法详解
2019/12/16 Javascript
JS实现滑动导航效果
2020/01/14 Javascript
ES6使用 Array.includes 处理多重条件用法实例分析
2020/03/02 Javascript
JavaScript 面向对象程序设计详解【类的创建、实例对象、构造函数、原型等】
2020/05/12 Javascript
vue实现导航菜单和编辑文本的示例代码
2020/07/04 Javascript
python中PIL安装简单教程
2016/04/21 Python
Python爬虫实现抓取京东店铺信息及下载图片功能示例
2018/08/07 Python
详解Python解决抓取内容乱码问题(decode和encode解码)
2019/03/29 Python
python批量爬取下载抖音视频
2019/06/17 Python
python多线程同步之文件读写控制
2021/02/25 Python
PyTorch 对应点相乘、矩阵相乘实例
2019/12/27 Python
基于Python脚本实现邮件报警功能
2020/05/20 Python
python如何写出表白程序
2020/06/01 Python
python实现AHP算法的方法实例(层次分析法)
2020/09/09 Python
python 实现音频叠加的示例
2020/10/29 Python
Expedia爱尔兰:酒店、机票、租车及廉价假期
2017/01/02 全球购物
高校学生干部的自我评价分享
2013/11/04 职场文书
小学生家长寄语
2014/04/02 职场文书
超市促销活动总结
2014/07/01 职场文书
预备党员入党感想
2015/08/10 职场文书
2015元旦感言
2015/12/09 职场文书
初一数学教学反思
2016/02/17 职场文书
css 边框添加四个角的实现代码
2021/10/16 HTML / CSS
解决vue中provide inject的响应式监听
2022/04/19 Vue.js