js设计模式-订阅-发布模式

本文深入探讨了JavaScript中的订阅-发布模式,通过宫颈癌疫苗接种预约实例,讲解了模式的定义、工作原理及其在实际场景中的应用。文章详细解释了如何通过事件对象实现对象间的解耦,并提供了代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

js设计模式-订阅-发布模式

最近在看js设计模式有关的书籍,所以想接着记录下比较常用的一些知识,就勉强算是一遍读书笔记吧

定义

发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。

生活中的例子

作为一个女生,最近很关心宫颈癌疫苗这个玩意,听说是一种预防宫颈癌发病的疫苗。自行出了这个疫苗,众多女性拍手一致叫好,毕竟人都怕死,万一哪天要game over了,却发现曾经有避免的机会,那必须后悔死呀-_-!!!。那么有了这个疫苗,女生势必是要关注的,但不是所有医院都能注射,即使医院能注射也不能保证有现货的了,何况疫苗还分个2,4,9价。最近小美就发现海淀医院可以注射这个疫苗,因此小美就每天都打电话过去问是否已经有了疫苗,但是除了小美还有小明,小欧等也会每天向医院询问,医院接电话的小姐姐差点决定辞职不干了,因为每天都是重复的问题。
不过都9012年了,自然是没有这么傻的处理方式,事实上是让每位有需求的客人都在医院中留下各自的信息,联系电话等,他们的信息都被医院记录在册,等医院有了疫苗就会通知各位。日常中还有卖楼及各种信息的推送都属于发布-订阅的模式
分析一下:在等待疫苗的例子中,使用发布订阅的模式有着许多显而易见的有点:

  • 顾客不用再每天都打电话咨询,在合适的时候医院就会发布出对应的消息进行告知
  • 医院也不需要关心顾客的其他信息,只要知道联系方式即可,不会有太多的耦合

实现这个小小的例子

  • 首先定义一个发布者(医院)
  • 发布者的缓存列表,存放回调函数方便通知订阅者(相当于医院的记录信息的册子)
  • 发布信息的时候,发布者遍历这个缓存列表,依次出发订阅者 的回调函数(挨个发消息)

回调函数中需要加上一些参数,订阅者可以接受这些参数,这些参数代表着医院给出的关于疫苗的价格,日期等这些附加信息

var hospitalCenter = {};	// 定义一个发布者 =》 医院疫苗处

hospitalCenter.infoBrochure = [];	// 定义医院疫苗处需要给发消息的名册

hospitalCenter.subscribe = function(fn) {	// 增加订阅者
	this.infoBrochure.push(fn)		// 订阅消息添加进缓存列表
}

hospitalCenter.pubulish = function () {	// 发布消息
	for(var i = 0, fn; fn = this.infoBrochure[i++];) {
		fn.apply(this, arguments)		// arguments发布消息时带上的参数
	}
}

// 小美订阅,小张都订阅了
hospitalCenter.subscribe(function(price, vaccineType) {

	console.log(vaccineType + '疫苗在此医院的价格为:' + price)
	
})

hospitalCenter.pubulish(10000, '二价')			
hospitalCenter.pubulish(20000, '四价')			

// 输出结果
// 二价疫苗在此医院的价格为:10000
// 四价疫苗在此医院的价格为:20000

再次我们看到了,小张和小美都是是订阅了这个医院的疫苗信息,因此医院在发布消息的时候,小美和小张都收到了,虽然说这样是实现了发布-订阅的功能,但是,发现了吗?他们是不分条件的收到了医院发出的所有信息,即使小美只关注二价疫苗,但依然收到了四价的,这样对于小美就是垃圾信息了。

我们优化下,增加一个标示 key, 让订阅者只订阅自己感兴趣的消息

// 按照订阅的的需求给发布消息
hospitalCenter.subscribe = function(key, fn) {			// key:订阅的消息类别
	if(!this.infoBrochure[key]) {							// 如果消息类别中无此类,就给该类消息创建一个缓存列表
		this.infoBrochure[key] = []
	}
	this.infoBrochure[key].push(fn)
}

hospitalCenter.pubulish = function() {
	var key = Array.prototype.shift.call(arguments),	// 取出消息类别
		fns = this.infoBrochure[key];						// 该消息类别下的回调集合
	if(!fns || fns.length === 0){							// 该类下无消息或没有订购此消息
		return false 
	}
	for(var i = 0, fn; fn=fns[i++];) {
		fn.apply(this, arguments)
	}

}	

// 小美订阅二价
hospitalCenter.subscribe('二价', function(price) {

	console.log('二价疫苗在此医院的价格为' + price)
	// console.log('squareMeter: ' +  squareMeter)
})

// 小张订阅四价
hospitalCenter.subscribe('四价', function(price) {

	console.log('四价疫苗在此医院的价格为' + price)
	// console.log('squareMeter: ' +  squareMeter)
})

// 小李订阅九价
hospitalCenter.subscribe('九价', function(price) {

	console.log('九价疫苗在此医院的价格为' + price)
	// console.log('squareMeter: ' +  squareMeter)
})

// 医院发布
hospitalCenter.pubulish('二价', 3000)
hospitalCenter.pubulish('四价',4000)
hospitalCenter.pubulish('九价',6000)

// 二价疫苗在此医院的价格为3000
// 四价疫苗在此医院的价格为4000
// 九价疫苗在此医院的价格为6000

这样的情况下,每位来医院的客人都能订阅自己只关注的的信息了
现在又有一个问题,北京的医院大大小小不计其数,小美离海淀医院近,便直接去了海淀医院,但是小张距离北大一医院近,因此决定直接去北大一。那么这段代码是不是在每个医院都得写上一遍呢??这样是不是太傻了点,我能不能直接让每个对象都拥有发布-订阅的功能?答案那必须的,接下来继续看:
我们定义一个事件:

var eventObj = {
	infoBrochure: [],
	subscribe: function(key , fn) {
		if(!this.infoBrochure[key]) {
			this.infoBrochure[key] = []
		}
		this.infoBrochure[key].push(fn)		// 订阅的消息添加进缓存列表
	},
	publish: function(){
		var key = Array.prototype.shift.call(arguments),
			fns = this.infoBrochure[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 = this.infoBrochure[key]
		if(!fns) {				// 如果 key 对应的消息没有被人订阅,则直接返回
			return false 
		}
		if(!fn) {
			fns && (fns.length = 0)
		} else {
			for(var j = fns.length; j >= 0; j--) {			// 反向遍历订阅的回调函数列表
				var fnsInfo = fns[j];
				if(fnsInfo === fn) {
					fns.splice(j,1)		// 删除订阅者的回调函数
				}
			}
		}
	}
}

var installEvent = function(obj) {
	for(var i in eventObj) {
		obj[i] = eventObj[i]
	}
}


var hospitalCenter = {}

installEvent(hospitalCenter)

hospitalCenter.subscribe('二价', fn1 = function(price){
	console.log('价格=' + price)
})

hospitalCenter.subscribe('四价', fn2 = function(price){
	console.log('四价价格=' + price)
})


hospitalCenter.remove('二价', fn1)

hospitalCenter.publish('四价', 5000)

// 四价价格=5000

这样的情况下,这个对象就具有了发布,订阅和删除的功能了
发布—订阅模式的优点非常明显,一为时间上的解耦,二为对象之间的解耦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值