js之发布 — 订阅模式
2016-12-03 09:47
405 查看
有人发布信息有人“订阅”信息并进行处理。
缺点:没有订阅的也会发来。
解决了level01的问题
接受者只要有相同key的的接受者都可以接受到信息。
等到有一天,项目中又新增了一个收货地址管理的模块,这个模块本来是另一个同事所写的,而此时你正在马来西亚度假,但是他却不得不给你打电话:“Hi,登录之后麻烦刷新一下收货地址列表。”于是你又翻开你 3个月前写的登录模块,在最后部分加上这行代码:
各自的业务处理,登录模块并不关心业务方究竟要做什么,也不想去了解它们的内部细节。改善后的代码如下:
比如现在有两个模块,a模块里面有一个按钮,每次点击按钮之后,b模块里的 div中会显示按钮的总点击次数,我们用全局发布 — 订阅模式完成下面的代码,使得 a 模块和 b 模块可以在保
持封装性的前提下进行通信。
即发布和订阅在不同的两个模块里。
DOM事件
document.body.addEventListener( 'click', function(){ alert(2); }, false ); document.body.click(); // 模拟用户点击
售楼处level01
售楼处发布楼盘信息,订阅者订阅(即把你需要的行为存入售楼处)这些信息。<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> var salesOffices = {}; // 定义售楼处 salesOffices.clientList = []; // 缓存列表,存放订阅者的回调函数(需要的行为)。 salesOffices.listen = function( fn ){ // 增加订阅者(添加需要的行为) this.clientList.push( fn ); // 订阅的消息添加进缓存列表 }; salesOffices.trigger = function(){ // 发布消息(依次执行添加了的行为) for( var i = 0, fn; fn = this.clientList[ i++ ]; ){ fn.apply( this, arguments ); // (2) // arguments 是发布消息时带上的参数 } }; // 下面我们来进行一些简单的测试: salesOffices.listen( function( price, squareMeter ){ // 小明订阅消息 console.log( '价格= ' + price ); console.log( 'squareMeter= ' + squareMeter ); }); salesOffices.trigger( 2000000, 88 ); // 输出:200 万,88 平方米 salesOffices.trigger( 3000000, 110 ); // 输出:300 万,110 平方米 </script> </body> </html>
缺点:没有订阅的也会发来。
售楼处level02
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> var salesOffices = {}; // 定义售楼处 salesOffices.clientList = {}; // 缓存列表,存放订阅者的回调函数 salesOffices.listen = function( key, fn ){ if ( !this.clientList[ key ] ){ // 如果还没有订阅过此类消息,给该类消息创建一个缓存列表 this.clientList[ key ] = []; } this.clientList[ key ].push( fn ); // 订阅的消息添加进消息缓存列表 }; salesOffices.trigger = function(){ // 发布消息 var key = Array.prototype.shift.call( arguments ), // 取出消息类型 fns = this.clientList[ key ]; // 取出该消息对应的回调函数集合 if ( !fns || fns.length === 0 ){ // 如果没有订阅该消息,则返回 return false; } for( var i = 0, fn; fn = fns[ i++ ]; ){ fn.apply( this, arguments ); // (2) // arguments 是发布消息时附送的参数 } }; salesOffices.listen( 'squareMeter88', function( price ){ // 小明订阅 88 平方米房子的消息 console.log( '价格= ' + price ); // 输出: 2000000 }); salesOffices.listen( 'squareMeter110', function( price ){ // 小红订阅 110 平方米房子的消息 console.log( '价格= ' + price ); // 输出: 3000000 }); salesOffices.trigger( 'squareMeter88', 2000000 ); // 发布 88 平方米房子的价格 salesOffices.trigger( 'squareMeter110', 3000000 ); // 发布 110 平方米房子的价格 </script> </body> </html>
解决了level01的问题
接受者只要有相同key的的接受者都可以接受到信息。
网站登录level01
login.succ(function(data){ header.setAvatar( data.avatar); // 设置 header 模块的头像 nav.setAvatar( data.avatar ); // 设置导航模块的头像 message.refresh(); // 刷新消息列表 cart.refresh(); // 刷新购物车列表 });
等到有一天,项目中又新增了一个收货地址管理的模块,这个模块本来是另一个同事所写的,而此时你正在马来西亚度假,但是他却不得不给你打电话:“Hi,登录之后麻烦刷新一下收货地址列表。”于是你又翻开你 3个月前写的登录模块,在最后部分加上这行代码:
login.succ(function(data){ header.setAvatar( data.avatar); // 设置 header 模块的头像 nav.setAvatar( data.avatar ); // 设置导航模块的头像 message.refresh(); // 刷新消息列表 cart.refresh(); // 刷新购物车列表 address.refresh(); // 增加这行代码 });
网站登录level02
用发布 — 订阅模式重写之后,对用户信息感兴趣的业务模块将自行订阅登录成功的消息事件。当登录成功时,登录模块只需要发布登录成功的消息,而业务方接受到消息之后,就会开始进行各自的业务处理,登录模块并不关心业务方究竟要做什么,也不想去了解它们的内部细节。改善后的代码如下:
<script>
/* login.succ(function(data){ header.setAvatar( data.avatar); // 设置 header 模块的头像 nav.setAvatar( data.avatar ); // 设置导航模块的头像 message.refresh(); // 刷新消息列表 cart.refresh(); // 刷新购物车列表 });*/
$.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(){ // nav 模块
login.listen( 'loginSucc', function( data ){
nav.setAvatar( data.avatar );
});
return {
setAvatar: function( avatar ){
console.log( '设置 nav 模块的头像' );
}
}
})();
/* 如果有一天在登录完成之
后,又增加一个刷新收货地址列表的行为,那么只要在收货地址模块里加上监听消息的方法即可,
而这可以让开发该模块的同事自己完成,你作为登录模块的开发者,永远不用再关心这些行为了。
代码如下:*/
var address = (function(){ // nav 模块
login.listen( 'loginSucc', function( obj ){
address.refresh( obj );
});
return {
refresh: function( avatar ){
console.log( '刷新收货地址列表' );
}
}
})();
售楼处 level03:全局的发布 - 订阅对象
发布 — 订阅模式可以用一个全局的 Event 对象来实现,订阅者不需要了解消息来自哪个发布者,发布者也不知道消息会推送给哪些订阅者, Event 作为一个类似“中介者”的角色,把订阅者和发布者联系起来。见如下代码:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> var Event = (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( var i = 0, fn; fn = fns[ i++ ]; ){ 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 = fns.length - 1; l >=0; l-- ){ var _fn = fns[ l ]; if ( _fn === fn ){ fns.splice( l, 1 ); } } } }; return { listen: listen, trigger: trigger, remove: remove } })(); Event.listen( 'squareMeter88', function( price ){ // 小红订阅消息 console.log( '价格= ' + price ); // 输出:'价格=2000000' }); Event.listen( 'squareMeter88', function( price ){ // 小红订阅消息 console.log( '价格= ' + price ); // 输出:'价格=2000000' }); Event.listen( 'squareMeter88', fn=function( price ){ // 小红订阅消息 console.log( '价格= ' + price ); // 输出:'价格=2000000' }); Event.remove( 'squareMeter88'); Event.trigger( 'squareMeter88', 2000000 ); // 售楼处发布消息 </script> </body> </html>
网站登录level04
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="PublishSubscribe.js"></script> </head> <body> <script> var event = { clientList: [], listen: function( key, fn ){ if ( !this.clientList[ key ] ){ this.clientList[ key ] = []; } this.clientList[ key ].push( fn ); // 订阅的消息添加进缓存列表 }, trigger: function(){ var key = Array.prototype.shift.call( arguments ), // (1); fns = this.clientList[ key ]; if ( !fns || fns.length === 0 ){ // 如果没有绑定对应的消息 return false; } for( var i = 0, fn; fn = fns[ i++ ]; ){ fn.apply( this, arguments ); // (2) // arguments 是 trigger 时带上的参数 } }, remove : function( key, fn ){ var fns = clientList[ key ]; if ( !fns ){ return false; } if ( !fn ){ fns && ( fns.length = 0 ); }else{ for ( var l = fns.length - 1; l >=0; l-- ){ var _fn = fns[ l ]; if ( _fn === fn ){ fns.splice( l, 1 ); } } } } }; var installEvent = function( obj ){ for ( var i in event ){ obj[ i ] = event[ i ]; } }; var salesOffices = {}; installEvent( salesOffices ); salesOffices.listen( 'squareMeter88', function( price ){ // 小明订阅消息 console.log( '价格= ' + price ); }); salesOffices.listen( 'squareMeter100', function( price ){ // 小红订阅消息 console.log( '价格= ' + price ); }); /*salesOffices.listen( 'squareMeter88', function( price ){ // 小红订阅消息 console.log( '价格= ' + price ); // 输出:'价格=2000000' }); salesOffices.listen( 'squareMeter88', fn=function( price ){ // 小红订阅消息 console.log( '价格= ' + price ); // 输出:'价格=2000000' }); Event.remove( 'squareMeter88');*/ salesOffices.trigger( 'squareMeter88', 2000000 ); // 输出:2000000 salesOffices.trigger( 'squareMeter100', 3000000 ); // 输出:3000000 </script> </body> </html>
模块间通信
上一节中实现的发布 — 订阅模式的实现,是基于一个全局的 Event 对象,我们利用它可以在两个封装良好的模块中进行通信,这两个模块可以完全不知道。比如现在有两个模块,a模块里面有一个按钮,每次点击按钮之后,b模块里的 div中会显示按钮的总点击次数,我们用全局发布 — 订阅模式完成下面的代码,使得 a 模块和 b 模块可以在保
持封装性的前提下进行通信。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="PublishSubscribe.js"></script> </head> <body> <button id="count">点我</button> <div id="show"></div> <script> var a = (function(){ var count = 0; var button = document.getElementById( 'count' ); button.onclick = function(){ Event.trigger( 'add', count++ ); } })(); var b = (function(){ var div = document.getElementById( 'show' ); Event.listen( 'add', function( count ){ div.innerHTML = count; }); })(); </script> </body> </html>
即发布和订阅在不同的两个模块里。
相关文章推荐
- js 发布订阅/观察者模式
- js全局的发布——订阅模式
- 基于JS原生的事件订阅和发布模式代码
- JS发布订阅模式
- 【JS】发布/订阅模式
- js设计模式之代理模式以及订阅发布模式
- node.js笔记之订阅发布设计模式
- js发布——订阅模式
- JS设计模式之发布订阅模式
- js 发布订阅模式
- [转载]JS中什么是发布--订阅模式?
- JS模式之发布/订阅模式
- js发布——订阅模式的通用实现及取消订阅
- node.js 发布订阅模式的实例
- js 发布/订阅模式
- JS设计模式 - 观察者模式与发布/订阅模式
- js:发布-订阅模式
- node.js 发布订阅模式
- js:发布订阅模式
- js事件编程的发布/订阅模式(一对一关系)