发布订阅者模式进化论
- 基础版本
- 第一步进化—仅订阅需要信息
- 第二步进化—通用型发布订阅
- 第三步进化—解耦发布者和订阅者关系
- 第四步进化—命名空间(防止代码越来越复杂,形成的全局冲突)
PS:发布订阅者模式可以先订阅,也可以先发布
发布订阅者模式的基础版
只要你绑定过DOM事件,你就已经是一个接触过发布-订阅模式的开发者。接下来一起来看看进化后的模式,如何带来更大的便利。
// 最简单的发布订阅模式 事件监听
document.body.addEventListener('click', function () {
alert(1);
})
document.body.addEventListener('click', function () {
alert(2)
})
document.body.addEventListener('click', function () {
alert(2)
})
简单实现
// 发布者----售楼处
var publish = {}
//时间缓存----电话号码 订阅者的回调函数
publish.eventList = []
//增加订阅者
publish.listen = function (fn) {
publish.eventList.push(fn) //加入订阅者到缓存列表
}
//发布消息
publish.triggle = function () {
for (var i = 0, fn; fn = this.eventList[i++];) {
fn.apply(this, arguments);
}
}
//订阅者-------买房人订阅消息
publish.listen(function (price, squareMeter) { //小明
console.log('价格', price);
console.log('squareMeter=', squareMeter);
})
publish.listen(function (price, squareMeter) { //小红
console.log('价格', price);
console.log('squareMeter=', squareMeter);
})
//发布
publish.triggle(10000, 88)
publish.triggle(50000, 108)
第一步进化
// 我们已经实现了简单的发布订阅者模式,但是存在个问题,即使小明并没有订阅小红108平的信息,但是也会推送给他,所以我们需要改造下,使用一个key来标识自己关注的信息
// 发布者----售楼处
var publish = {}
//时间缓存----电话号码 订阅者的回调函数
publish.eventList = []
//增加订阅者
publish.listen = function (key, fn) {
if (!this.eventList[key]) { //如果没有订阅过此类消息,则给该类消息添加缓存列表
this.eventList[key] = [];
}
publish.eventList[key].push(fn) //加入订阅者到缓存列表
}
//发布消息
publish.triggle = function () {
var key = Array.prototype.shift.call(arguments), //取出消息类型
fns = this.eventList[key]; //取出消息类型对应的消息处理函数
if (!fns || fns.length === 0) {
return false; //如果没有消息对应的处理函数则返回
}
for (var i = 0, fn; fn = fns[i++];) { //循环ket对应的消息列表处理函数
fn.apply(this, arguments);
}
}
//订阅者-------买房人订阅消息
publish.listen('squareMeter88', function (price, map) { //小明
console.log('价格', price);
console.log('squareMeter=', map);
})
publish.listen('squareMeter108', function (price, map) { //小红
console.log('价格', price);
console.log('squareMeter=', map);
})
//发布
publish.triggle("squareMeter88", 10000, '北京')
publish.triggle('squareMeter108', 50000, '北京')
第二步进化
// 通用模式,让所有售楼处都有发布订阅的功能
var event = {
eventList: [],
listen: function (key, fn) {
if (!this.eventList[key]) { //如果没有订阅过此类消息,则给该类消息添加缓存列表
this.eventList[key] = [];
}
this.eventList[key].push(fn) //加入订阅者到缓存列表
},
triggle: function () {
var key = Array.prototype.shift.call(arguments), //取出消息类型
fns = this.eventList[key]; //取出消息类型对应的消息处理函数
if (!fns || fns.length === 0) {
return false; //如果没有消息对应的处理函数则返回
}
for (var i = 0, fn; fn = fns[i++];) { //循环ket对应的消息列表处理函数
fn.apply(this, arguments);
}
},
remove: function (key, fn) {
var fns = this.event[key];
if (!fns) {
return false;
}
if (!fn) { //如果为传入处理的回调函数,则表示所有都需要取消 即销毁订阅模式
fns && (fns.length = 0);
} else {
for (var len = fns.length - 1; len >= 0; len--) { //反向遍历事件缓存列表
var _fn = fns[len];
if (_fn == fn) {
fns.splice(len, 1);//删除订阅者的处理回调函数
}
}
}
}
}
//定义一个给对象安装发布订阅功能的函数
var installEvent = function (obj) {
//浅复制 实现对象复制
for (var i in event) {
obj[i] = event[i];
}
}
//发布
var sales = {}
installEvent(sales);
//订阅者-------买房人订阅消息
sales.listen('squareMeter88', function (price, map) { //小明
console.log('价格', price);
console.log('squareMeter=', map);
})
sales.listen('squareMeter108', function (price, map) { //小红
console.log('价格', price);
console.log('squareMeter=', map);
})
//发布
sales.triggle("squareMeter88", 10000, '北京')
sales.triggle('squareMeter108', 50000, '北京')
第三步进化
//如果说小明不只是关注sales1的88平米房子,还需要关系sales2的300平米房子 那小明就还需要订阅sales的事件,此时引出‘中介者’ 便可以方便解决此问题 只需要订阅一次即可
//实现无需知道是哪个sales发布的消息 只需要关注订阅者即可 解耦发布者无需关系推给谁 订阅者无需知道是由谁发布
// 作为中介者,让所有售楼处都有发布订阅的功能,订阅者只需要订阅一次信息
var Event = (function(){
var eventList = {},
listen,
triggle,
remove;
listen=function (key, fn) {
if (!eventList[key]) { //如果没有订阅过此类消息,则给该类消息添加缓存列表
eventList[key] = [];
}
eventList[key].push(fn) //加入订阅者到缓存列表
},
triggle= function () {
var key = Array.prototype.shift.call(arguments), //取出消息类型
fns = this.eventList[key]; //取出消息类型对应的消息处理函数
if (!fns || fns.length === 0) {
return false; //如果没有消息对应的处理函数则返回
}
for (var i = 0, fn; fn = fns[i++];) { //循环ket对应的消息列表处理函数
fn.apply(this, arguments);
}
},
remove=function (key, fn) {
var fns = eventList[key];
if (!fns) {
return false;
}
if (!fn) { //如果为传入处理的回调函数,则表示所有都需要取消 即销毁订阅模式
fns && (fns.length = 0);
} else {
for (var len = fns.length - 1; len >= 0; len--) { //反向遍历事件缓存列表
var _fn = fns[len];
if (_fn == fn) {
fns.splice(len, 1);//删除订阅者的处理回调函数
}
}
}
}
return {
listen,
triggle,
remove
}
})()
//订阅者-------买房人订阅消息
Event.listen('squareMeter88', function (price) { //小明
console.log('价格', price);
})
//发布
Event.triggle('squareMeter108', 50000)
第四步进化
这种进化,属于高级,放你了解以上三种进化后,我会努力的推送给你们,静待更新喽~~~~~~~
关注更新,提升技术~~~~~~~~
欢迎star本人github:https://github.com/flyku