1,什么是发布订阅模式
发布订阅模式其实是一种对象之间一对多的依赖关系(利用消息队列)。当一个对象的状态发生改变时,所有依赖它的对象都得到状态的改变的通知。订阅者(subscriber)把自己想订阅的事件注册到调度中心(Event Channel),当发布者(Publisher)发布该事件(publish Event)到调用中心,也就是该事件触发时,由调度中心统一调度(Fire Event) 订阅过者注册到调度中心处理代码。
完成整个发布订阅流程一共有3个
一,发布者
二,事件中心
三,订阅者
个人理解:有很多学生(相当于订阅者)需要收到学校的通知,但是需要先进老师创建的群(相当于事件中心)。当老师(发布者)在群里发送群消息的时候,以群消息的形式通知所有的学生,这时所有的学生都能收到消息。
2,如何实现发布订阅模式
基本思路
1,创建一个对象 2,创建一个缓存列表相当于(调度中心) 3,创建on方法用来把函数fn都添加到缓存列表中 4,创建emit方法取到arguments里第一个当做event,根据event值去执行对应缓存列表中的函数(发布都发布事件到调度中心,调度中心去进行对应的处理)
实现1
<script>
//可以理解为调度中心
let ObjPublisher={}
//缓存列表
ObjPublisher.list={}
console.log(ObjPublisher)
//进行订阅
ObjPublisher.on=function(event,fn){
let that = this;
//如果对象中没有对应的event值,也就是没有订阅过,就给event创建一个缓存列表
//如果对象中有相应的event值,把fn添加到对应的event的缓存列表里
(that.list[event]||(that.list[event]=[])).push(fn)
return that
}
//发布
ObjPublisher.emit=function () {
let that = this;
//第一个参数是对应的event值,直接用数组的shift方法取出
let event=[].shift.call(arguments),fns=[...that.list[event]]
//如果返回列表里面没有nf就返回false
if(!fns||fns.length===0){
return false
}
//遍历event值对应的缓存列表,依次执行fn
fns.forEach(fn=>{
fn.apply(that,arguments)
})
return that
}
function user1(content) {
console.log("张三",content)
}
function user2(content) {
console.log("李四",content)
}
function user3(content) {
console.log("王五",content)
}
function user4(content) {
console.log("赵六",content)
}
//订阅
ObjPublisher.on('asdf',user1)
ObjPublisher.on('asdf',user2)
ObjPublisher.on('jkl',user3)
ObjPublisher.on('jkl',user4)
//发布
ObjPublisher.emit('asdf','js发布订阅模式')
ObjPublisher.emit('jkl','我是王五')
//输出
//张三 js发布订阅模式
//李四 js发布订阅模式
//王五 我是王五
//赵六 我是王五
</script>
实现2
<script>
/*
* 向消息队列里面添加内容
* 移除消息队列里面的内容
* 触发消息队列里面的内容
* */
class Observer{
constructor() {
this.message={}
}
//type要传入的数据类型
//fn行为发生之后要做的事情
on(type,fn){
//先判断有没有这个属性
if(!this.message[type]){
//如果没有这个属性,就初始化一个空数组
this.message[type]=[]
}
//如果没有这个属性,就往它的后面push一个新的fn
this.message[type].push(fn)
}
//取消订阅
off(type,fn){
//1,先判断有没有订阅
if(!this.message[type]) return
//2,判断有没有fn这个参数
if(!fn){
//如果没有fn就删除掉整个事件
this.message[type]=undefined
return
}
//如果有fn就只是仅仅过滤掉这个方法
this.message[type] = this.message[type].filter((item)=>item!==fn)
}
//进行
emit(type){
//判断有没有订阅
if(!this.message[type]) return
this.message[type].forEach((item)=>{
item()//执行这个方法
})
}
}
//使用构造函数创建一个实例
const Person1 = new Observer();
//我现在想要向person1委托一些内容,帮我进行观察
//例子1
console.log(Person1)
Person1.on('fun',fun1)
Person1.on('fun',fun2)
Person1.on('fun',fun3)
Person1.off('fun',fun2)//取消事件2
//进行触发
Person1.emit('fun')
function fun1(){
console.log("我是事件1")
}
function fun2(){
console.log("我是事件2")
}
function fun3(){
console.log("我是事件3")
}
//输出
//我是事件1
//我是事件3
//例子2
Person1.on('js',f1)
Person1.on('js',f2)
Person1.on('js',f3)
Person1.emit('js')
function f1(){
console.log("张三")
}
function f2(){
console.log("李四")
}
function f3(){
console.log("王五")
}
//输出张三,李四,王五
</script>
3,优点和缺点
优点
1,对象之间可以解耦
2,异步编程中,可以更松耦合的代码编写
缺点
1,创建订阅者本身要消耗一定的时间和内存
2,能弱化对象之间的联系,多个发布者和订阅者嵌套一起的时候,程序难以维护