模式的概念
最近我的小伙伴们在面试中,这两个被问的很频繁,斗胆的跟大家分享一下我的理解。我将采用“官方解释”+“大白话”的方式。
熟悉Vue的小伙伴都知道,在Vue中使用“观察者模式”去通知视图更新,使用“发布订阅”$on,$emit
来实现自定义事件,所以搞清楚两者之间的关系很重要啦!
观察者模式:一个对象(观察者)订阅另一个对象(主题),当主题被激活的时候,触发观察者里面的事件。
大白话解释:当去你去医院打吊瓶(某些地方叫打点滴,就是这么严谨)的时候,医生要观察吊瓶的改变,当快打完的时候,就要通知医生来取针。这里医生就是观察者(Obeserver),吊瓶就是被观察者/主题。
class Subject { //被观察者数据---吊瓶
constructor(name="知了哥"){
this.state = 100;
this.name = name;
this.obs = [] //会有多个观察者
}
addObs(ob){
this.obs.push(ob)
}
setState(state){ //改变状态的方法
this.state = state
//要去通知观察者去做动作,--- 拔针
this.obs.forEach((ob)=>{
ob.update(this) //让观察者去做更新
})
}
}
class Obeserver{ //观察者 -- 医生
constructor(name){
this.name = name;
//在观察者里面是不是也可以记录 观察了哪些 数据(吊瓶)
}
update(subject){
//医生也有可能观察多个 打针的人
if (!subject.state) {
console.log(`${this.name} 收到通知 :${subject.name} 的 带瓶打完啦!`)
}else {
console.log(` ${this.name} 收到通知 :${subject.name} 的 带瓶量: ${subject.state}!`)
}
}
}
var zhiliao = new Subject();
var hushi = new Obeserver("护士");
var yisheng = new Obeserver("医生")
zhiliao.addObs(hushi) //把观察者放到被观察这的里面去
zhiliao.addObs(yisheng)
zhiliao.setState(50)
发布订阅:订阅者把自己想要订阅的事件注册到调度中心,当发布者发布事件到调度中心(就是该事件被触发),再由调度中心统一调度订阅者注册到调度中心的处理代码。
大白话解释:其实简单理解就是我们的自定义事件,比如在Vue中,Vue实例($bus
)就是统一的调用中心,我们使用$bus
去$on
一个自定义事件myEvent
就是发布者发布一个事件到调度中心,然后在其他地方$bus.$emit(myEvent)
一下就相当于事件被触发,然后$bus
就去执行对应事件的回调函数 。哈哈,好绕口!再比如毛十八去邮局里面订阅报纸,订阅了南方都市报,那南方都市报就是我们的发布者,当发布新报纸的时候,邮局再去通知所有的订阅者取报纸啊!
var Event = {
_listeners: {},
// 添加
$on: function(type, fn) {
if (typeof this._listeners[type] === "undefined") {
this._listeners[type] = [];
}
if (typeof fn === "function") {
this._listeners[type].push(fn);
}
return this;
},
// 触发
$emit: function(type) {
var arrayEvent = this._listeners[type];
if (arrayEvent instanceof Array) {
for (var i=0, length=arrayEvent.length; i<length; i+=1) {
if (typeof arrayEvent[i] === "function") {
arrayEvent[i]({ type: type });
}
}
}
return this;
},
// 删除
$off: function(type, fn) {
var arrayEvent = this._listeners[type];
if (typeof type === "string" && arrayEvent instanceof Array) {
if (typeof fn === "function") {
for (var i=0, length=arrayEvent.length; i<length; i+=1){
if (arrayEvent[i] === fn){
this._listeners[type].splice(i, 1);
break;
}
}
} else {
delete this._listeners[type];
}
}
return this;
}
};
总结:学习这两种模式,其实就是为了 代码解耦 ,让每个独立的对象分开。小伙伴需要从日常的业务场景去观察,比如在分页插件中,就可以利用“发布订阅”为点击每一个分页数的时候触发($emit
)一个自定义事件,如果需要在点击分页的是做一些其他的时候,就可以在外面$on
这个事件,那么分页插件就可以很好的封装起来,不用给里面传递参数或者回调函数之类的了。