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 相关文章推荐
一个XML格式数据转换为图表的例子
Feb 09 Javascript
append和appendTo的区别以及appendChild用法
Dec 24 Javascript
Javascript学习笔记之 函数篇(一) : 函数声明和函数表达式
Jun 24 Javascript
jQuery实现隔行背景色变色
Nov 24 Javascript
在JavaScript中操作时间之setYear()方法的使用
Jun 12 Javascript
jQuery无缝轮播图代码
Dec 22 Javascript
react开发中如何使用require.ensure加载es6风格的组件
May 09 Javascript
BootStrap selectpicker后台动态绑定数据
Jun 01 Javascript
BootstrapTable加载按钮功能实例代码详解
Sep 22 Javascript
JavaScript new对象的四个过程实例浅析
Jul 31 Javascript
layui实现下拉复选功能的例子(包括数据的回显与上传)
Sep 24 Javascript
详解element-ui动态限定的日期范围选择器代码片段
Jul 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二分法在IP地址查询中的应用
2008/08/12 PHP
修改PHP的memory_limit限制的方法分享
2012/02/21 PHP
thinkphp3.2实现在线留言提交验证码功能
2017/07/19 PHP
利用php操作memcache缓存的基础方法示例
2017/08/02 PHP
this[] 指的是什么内容 讨论
2007/03/24 Javascript
js取两个数组的交集|差集|并集|补集|去重示例代码
2013/08/07 Javascript
基于JS代码实现实时显示系统时间
2016/06/16 Javascript
AngularJS基础 ng-cloak 指令简单示例
2016/08/01 Javascript
基于ajax与msmq技术的消息推送功能实现代码
2016/12/26 Javascript
浅谈JS验证表单文本域输入空格的问题
2017/02/14 Javascript
Vuejs 页面的区域化与组件封装的实现
2017/09/11 Javascript
浅谈react-router@4.0 使用方法和源码分析
2019/06/04 Javascript
微信小程序解析富文本过程详解
2019/07/13 Javascript
vue实现图片上传功能
2020/05/28 Javascript
JavaScript实现图片放大预览效果
2020/11/02 Javascript
[54:47]Liquid vs VP Supermajor决赛 BO 第五场 6.10
2018/07/05 DOTA
python encode和decode的妙用
2009/09/02 Python
python每隔N秒运行指定函数的方法
2015/03/16 Python
安装dbus-python的简要教程
2015/05/05 Python
python基础教程之五种数据类型详解
2017/01/12 Python
TensorFlow 实战之实现卷积神经网络的实例讲解
2018/02/26 Python
PyQt5每天必学之日历控件QCalendarWidget
2018/04/19 Python
python写入已存在的excel数据实例
2018/05/03 Python
python版opencv摄像头人脸实时检测方法
2018/08/03 Python
使用Django简单编写一个XSS平台的方法步骤
2019/03/25 Python
python3利用Socket实现通信的方法示例
2019/05/06 Python
使用python求解二次规划的问题
2020/02/29 Python
Python爬虫headers处理及网络超时问题解决方案
2020/06/19 Python
CSS3改变浏览器滚动条样式
2019/01/04 HTML / CSS
介绍Ibatis的核心类
2013/11/18 面试题
大学生职业规划范文:象牙塔生活的四年计划
2014/01/14 职场文书
渡河少年教学反思
2014/02/12 职场文书
大学生活自我评价
2014/04/09 职场文书
个人批评与自我批评范文
2014/10/17 职场文书
抄袭同学作业检讨书1000字
2014/11/20 职场文书
英文产品推荐信
2015/03/27 职场文书