发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。在 JavaScript 开发中,我们一般用事件模型 来替代传统的发布—订阅模式。

故事背景

小明最近看上了一套房子,到了售楼处之后才被告知,该楼盘的房子早已售罄。好在售楼 MM 告诉小明,不久后还有一些尾盘推出,开发商正在办理相关手续,手续办好后便可以购买。 但到底是什么时候,目前还没有人能够知道。 于是小明记下了售楼处的电话,以后每天都会打电话过去询问是不是已经到了购买时间。除 了小明,还有小红、小强、小龙也会每天向售楼处咨询这个问题。一个星期过后,售楼 MM 决 定辞职,因为厌倦了每天回答 1000 个相同内容的电话。 当然现实中没有这么笨的销售公司,实际上故事是这样的:小明离开之前,把电话号码留在 了售楼处。售楼 MM 答应他,新楼盘一推出就马上发信息通知小明。小红、小强和小龙也是一 样,他们的电话号码都被记在售楼处的花名册上,新楼盘推出的时候,售楼 MM 会翻开花名册,遍历上面的电话号码,依次发送一条短信来通知他们。

发送短信通知就是一个典型的发布—订阅模式,小明、小红等购买者都是 订阅者,他们订阅了房子开售的消息。售楼处作为发布者,会在合适的时候遍历花名册上的电话号码,依次给购房者发布消息。

代码实现

  • 先订阅后发布模式
var DEvent = (function() {var clientList = {},listen,trigger,remove;listen = function(key, fn) {if (!clientList[key]) {clientList[key] = [];}clientList[key].push(fn);};trigger = function() {var key = Array.prototype.shift.call(arguments),fns = clientList[key];if (!fns || fns.length === 0) {return false;}for (let index = 0; index < fns.length; index++) {const fn = fns[index];fn.apply(this, arguments);}};remove = function(key, fn) {var fns = clientList[key];if (!fns) {return false;}if (!fn) {fns && (fns.length = 0);} else {for (var l = fn.length - 1; l > 0 ; l--) {var _fn = fns[l];if (_fn === fn) {fns.splice(l, 1);}}}};return {listen,trigger,remove};
})();
Event.listen( 'squareMeter88', function( price ){ // 小红订阅消息console.log( '价格= ' + price );  // 输出:'价格=2000000'
});
Event.trigger( 'squareMeter88', 2000000 );// 售楼处发布消息
复制代码

应用场景

  • 网站登录 假如我们正在开发一个商城网站,网站里有 header 头部、nav 导航、消息列表、购物车等模块。这几个模块的渲染有一个共同的前提条件,就是必须先用 ajax 异步请求获取用户的登录信息。 这是很正常的,比如用户的名字和头像要显示在 header 模块里,而这两个字段都来自用户登录后 返回的信息。 至于 ajax 请求什么时候能成功返回用户信息,这点我们没有办法确定。现在的情节看起来像 极了售楼处的例子,小明不知道什么时候开发商的售楼手续能够成功办下来。
$.ajax( 'http:// xxx.com?login', function(data){ // 登录成功 login.trigger('loginSucc', data); // 发布登录成功的消息
});
var header = (function(){ // header 模块 login.listen( 'loginSucc', function( data){header.setAvatar( data.avatar );}); return {setAvatar: function( data ){console.log( '设置 header 模块的头像' );} }
})();var nav = (function(){login.listen( 'loginSucc', function( data ){// nav 模块 nav.setAvatar( data.avatar );}); return {setAvatar: function( avatar ){ console.log( '设置 nav 模块的头像' );} }
})();
复制代码
  • 先发布后订阅模式(提供创建命名空间的功能)
var Event = (function(){var global = this, Event,_default = 'default';Event = function(){var _listen,_trigger,_remove,_slice = Array.prototype.slice, _shift = Array.prototype.shift, _unshift = Array.prototype.unshift, namespaceCache = {},_create,find,each = function( ary, fn ){var ret;for ( var i = 0, l = ary.length; i < l; i++ ){var n = ary[i];ret = fn.call( n, i, n); }return ret; };_listen = function( key, fn, cache ){ if ( !cache[ key ] ){cache[ key ] = []; }cache[key].push( fn );};_remove = function( key, cache, fn) {if ( cache[ key ] ){if( fn ){for( var i = cache[ key ].length; i >= 0; i-- ){if( cache[ key] [i] === fn) {cache[key].splice(i, 1);}} } else{cache[ key ] = [];}} };_trigger = function(){var cache = _shift.call(arguments),key = _shift.call(arguments), args = arguments,_self = this, ret, stack = cache[ key ];if ( !stack || !stack.length ) {return;}return each( stack, function(){return this.apply( _self, args );}); };_create = function( namespace ){var namespace = namespace || _default;var cache = {},offlineStack = [],// 离线事件ret = {listen: function(key, fn, last ){_listen(key, fn, cache );if ( offlineStack === null ){return; }if ( last === 'last' ){offlineStack.length && offlineStack.pop()(); }else{each( offlineStack, function(){this(); });}offlineStack = null; },one: function( key, fn, last ){ _remove( key, cache ); this.listen( key, fn ,last );},remove: function( key, fn ){_remove( key, cache ,fn);},trigger: function(){var fn, args,_self = this;_unshift.call( arguments, cache ); args = arguments;fn = function(){return _trigger.apply( _self, args ); };if ( offlineStack ){return offlineStack.push( fn );}return fn(); }};return namespace ?( namespaceCache[ namespace ] ? namespaceCache[ namespace] :namespaceCache[ namespace ] = ret ) : ret;};return {create: _create,one: function( key,fn, last ){ var event = this.create( );event.one( key,fn,last );},remove: function( key,fn ){var event = this.create( ); event.remove( key,fn );},listen: function( key, fn, last ){var event = this.create( ); event.listen( key, fn, last );},trigger: function(){var event = this.create( );event.trigger.apply( this, arguments ); }}; }();return Event;
})();
复制代码

系列文章:

《JavaScript设计模式与开发实践》最全知识点汇总大全

《JavaScript设计模式与开发实践》模式篇(5)—— 观察者模式相关推荐

  1. 《JavaScript设计模式与开发实践》模式篇(12)—— 装饰者模式

    在传统的面向对象语言中,给对象添加功能常常使用继承的方式,但是继承的方式并不灵活, 还会带来许多问题:一方面会导致超类和子类之间存在强耦合性,当超类改变时,子类也会随之 改变;另一方面,继承这种功能复 ...

  2. 《JavaScript设计模式与开发实践》模式篇(3)—— 代理模式

    代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问 故事背景: 假设当 A 在心情好的时候收到花,小明表白成功的几率有 60%,而当 A 在心情差的时候收到花,小明表白的成功率无限趋近于 ...

  3. JS代理模式《JavaScript设计模式与开发实践》阅读笔记

    代理模式 代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问. 保护代理和虚拟代理 保护代理:当有许多需求要向某对象发出一些请求时,可以设置保护代理,通过一些条件判断对请求进行过滤. 虚拟 ...

  4. 《JavaScript设计模式与开发实践》原则篇(3)—— 开放-封闭原则

    在面向对象的程序设计中,开放封闭原则(OCP)是最重要的一条原则.很多时候,一个程序具有良好的设计,往往说明它是符合开放封闭原则的. 当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增 ...

  5. JavaScript设计模式与开发实践(网课学习)

    Js设计模式与开发实践 面向对象 5大设计原则 23种设计模式(实际只有21种) 设计模式主要分为下面三大类 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模 ...

  6. JavaScript设计模式与开发实践——JavaScript的多态

    "多态"一词源于希腊文polymorphism,拆开来看是poly(复数)+ morph(形态)+ ism,从字面上我们可以理解为复数形态. 多态的实际含义是:同一操作作用于不同的 ...

  7. 《JavaScript设计模式与开发实践》阅读摘要

    <JavaScript设计模式与开发实践>作者:曾探 系统的介绍了各种模式,以及js中的实现.应用,以及超大量高质量代码,绝对值得一读 面向对象的js 静态类型:编译时便已确定变量的类型 ...

  8. 专访《Javascript设计模式与开发实践》作者曾探:爱编程 爱生活

     专访<Javascript设计模式与开发实践>作者曾探:爱编程 爱生活 发表于12小时前| 2742次阅读| 来源CSDN| 8 条评论| 作者夏梦竹 专访曾探图书作者Javascr ...

  9. JavaScript设计模式与开发实践系列之单例模式

    本系列为<JavaScript设计模式与开发实践>(作者:曾探)学习总结,如想深入了解,请支持作者原版 单例模式 实现单例模式 单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的 ...

最新文章

  1. Redis 几种应用场景
  2. 全新的Play模块资料库
  3. 判断用户用手机访问还是用电脑访问网页
  4. 课时4:改进我们的小游戏
  5. 算法笔记二分查找题目
  6. 泰坦尼克号数据集处理
  7. 综合项目之闪讯破解(五)之 如何用C#调用C++编写的Dll
  8. web前端开发面试题(六)
  9. oracle设计案例,Oracle课程设计案例精编
  10. Win7下硬盘安装Ubuntu-16.04 LTS教程
  11. python获取word页数_使用Python的word文档的页数(Number of pages of a word document with Python)...
  12. mac 查看端口_交换机端口对应的mac地址与IP地址
  13. [汇编] 汇编语言实现简易文本编辑器(光标移动、上卷和退格删除)
  14. 在GitHub上搭建个人主页
  15. gulp仿移动端网易云音乐播放界面
  16. Android中禁止WebView滑动
  17. 屡陷丑闻的 Facebook,试图靠 AI Bot 管住员工的嘴
  18. Python爬虫编程思想(70): 项目实战--抓取京东商城手机销售排行榜
  19. 艰难的起步---微信小程序访问MSSQL数据库实例
  20. Emlog采集插件 刀网资源采集 一键显示资源1.1

热门文章

  1. Android KitKat 4.4 Wifi移植AP模式和网络共享的调试日志
  2. ExtJS中给Tree节点加click事件
  3. BZOJ-1008 越狱
  4. 用setTimeout实现setInterval的功能
  5. 多個excel文件合并到一個excel文件
  6. eclipse手动pom本地包_环境篇--Eclipse如何远程连接Hadoop集群调试
  7. 信号通路:PI3K信号通路与PI3Kα抑制剂
  8. adams2016安装教程
  9. 计算机视觉与深度学习 | 开源SLAM、视觉里程计综述(SLAM、Visual Odometry)
  10. 数据结构学习笔记(五):重识字符串(String)