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 相关文章推荐
如何让动态插入的javascript脚本代码跑起来。
Jan 09 Javascript
网页自动跳转代码收集
Sep 27 Javascript
javascript获取下拉列表框当中的文本值示例代码
Jul 31 Javascript
javascript几个易错点记录
Nov 26 Javascript
javascript关于继承解析
May 10 Javascript
Google 地图API资料整理及详细介绍
Aug 06 Javascript
select隐藏选中值对应的id,显示其它id的简单实现方法
Aug 25 Javascript
Windows环境下npm install 报错: operation not permitted, rename的解决方法
Sep 26 Javascript
vue富文本编辑器组件vue-quill-edit使用教程
Sep 21 Javascript
Electron中实现大文件上传和断点续传功能
Oct 28 Javascript
微信小程序学习笔记之函数定义、页面渲染图文详解
Mar 28 Javascript
JavaScript+HTML实现学生信息管理系统
Apr 20 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
全国FM电台频率大全 - 4 山西省
2020/03/11 无线电
基础的WordPress插件制作教程
2015/11/24 PHP
thinkphp3.x中session方法的用法分析
2016/05/20 PHP
详细解读php的命名空间(一)
2018/02/21 PHP
JavaScript计时器示例分析
2015/02/05 Javascript
jQuery使用animate创建动画用法实例
2015/08/07 Javascript
JS简单实现点击复制链接的方法
2016/08/03 Javascript
Javascript使用uploadify来实现多文件上传
2016/11/16 Javascript
详解Vue.js动态绑定class
2016/12/20 Javascript
Vue组件tree实现树形菜单
2017/04/13 Javascript
nodejs入门教程一:概念与用法简介
2017/04/24 NodeJs
使用vue和datatables进行表格的服务器端分页实例代码
2017/06/07 Javascript
node+express+ejs使用模版引擎做的一个示例demo
2017/09/18 Javascript
vue-cli+webpack项目 修改项目名称的方法
2018/02/28 Javascript
jQuery pagination分页示例详解
2018/10/23 jQuery
详解微信小程序之提高应用速度小技巧
2020/01/07 Javascript
Vue数字输入框组件示例代码详解
2020/01/15 Javascript
javascript设计模式 ? 外观模式原理与用法实例分析
2020/04/15 Javascript
taro 实现购物车逻辑的实例代码
2020/06/05 Javascript
js编写简易的计算器
2020/07/29 Javascript
[44:47]Ti4 循环赛第三日 iG vs NaVi
2014/07/12 DOTA
[48:02]Ti4循环赛第三日 VG vs Liquid和NEWBEE vs DK
2014/07/12 DOTA
详细讲解Python中的文件I/O操作
2015/05/24 Python
python爬虫之自动登录与验证码识别
2020/06/15 Python
python调用c++传递数组的实例
2019/02/13 Python
OpenCV Python实现拼图小游戏
2020/03/23 Python
Python读取配置文件(config.ini)以及写入配置文件
2020/04/08 Python
Django与AJAX实现网页动态数据显示的示例代码
2021/02/24 Python
自荐信格式的六要素
2013/09/21 职场文书
高中生毕业自我鉴定
2013/10/10 职场文书
银行求职信个人范文
2013/12/16 职场文书
环保建议书200字
2014/05/14 职场文书
夫妻忠诚协议范文
2014/11/16 职场文书
2015年安康杯竞赛活动总结
2015/03/26 职场文书
2015年小学校长工作总结
2015/05/19 职场文书
Vue中foreach数组与js中遍历数组的写法说明
2021/06/05 Vue.js