小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
今天我来讲一下咱们的设计模式中的观察者模式哈,或者说是订阅-发布模式
这次的课程分三个部分哈,首先第一部分我们先讲述一下观察者模式的定义,第二部分呢我们会讲述一下观察者模式在js中的应用,第三部分呢我们使用原生js实现一个观察者模式
先抛概念
观察者模式(Observer)通常又被称为 发布-订阅者模式 或 消息机制,它定义了对象间的一种一对多的依赖关系,只要当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,解决了主体对象与观察者之间功能的耦合,即一个对象状态改变给其他对象通知的问题。
我们举个例子
我要点外卖,但是外卖太火关店了,得客流量下来才能开店,但是我又不知道什么时候开店,所以我要使用一个功能,我们就称之为订购哈,我先选好了我要什么,送到哪里,然后让外卖开店的时候给我自动下单,
这样呢 外卖店也不怕因为临时关店丢到了订单,我也不会因为无法下单而吃不上饭
这就是观察者模式
观察者模式在js中的应用
最典型的就是我们每个人都使用过的dom监听,比如下面的代码
document.querySelector('.btn').addEventListener('click',function () {
alert('You click this btn');
},false)
没错,我们平时对 DOM 的事件绑定就是一个非常典型的 发布-订阅者模式,这里我们需要监听用户点击按钮这个动作,但是我们却无法知道用户什么时候去点击,所以我们订阅 按钮上的 click 事件,只要按钮被点击时,那么按钮就会向订阅者发布这个消息,我们就可以做对应的操作了。
除了我们常见的 DOM 事件绑定外,观察者模式应用的范围还有很多~
比如比较当下热门 vue 框架,里面不少地方都涉及到了观察者模式,比如数据和视图的绑定
大致的实现是设置一个监听器 Observer,用来监听所有属性,会给每个订阅者Watcher绑定上指令解析器 Compile 解析对应的指令生成的update方法。如果属性上发上变化了,就需要告诉订阅者 Watcher 去执行update方法更新视图。
还有就是子组件与父组件通信
Vue 中我们通过 props 完成父组件向子组件传递数据,子组件与父组件通信我们通过自定义事件即 $on,$emit来实现,其实也就是通过 $emit 来发布消息,并对订阅者 $on 做统一处理。
ok,说了这么多,该我们自己露一手了,接下来我们来自己创建一个简单的观察者。
首先我们创建一个观察者对象,这里为了好理解我们使用订阅者和发布者来进行下面的讲述,来对应观察者和被观察者
const Observe = {}
这个发布者对象至少可以订阅是吧,要告诉这个对象当外卖店开门了要给我下订单对吧。我们称这个方法为on
const Observe = {
on(type, fn) {}
}
他接收两个参数,一个是事件类型,也就是开店或者关店什么什么的,另一个就是当事件发生时的回调。也就是开店了之后我要做什么,这里我们就发现了,我们可以订阅很多的这个事件,比如开店了我点外卖,那还有别人点奶茶什么的。当开店发生的时候,这些绑定的回调都是需要执行的,所以这里需要一个存储回调的队列。我们称之为queue。
const Observe = {
queue: {},
on(type, fn) {
if (typeof this.queue[type] === 'undefined') {
// 将执行方法推入该事件对应的执行队列中
this.queue[type] = [fn];
}
else {
// 如果此消息存在,直接将执行方法推入该事件对应的执行队列中
this.queue[type].push(fn);
}
}
}
这里我们每一种事件的类型,都绑定了一个回调的数组。当事件发生的时候我们应该挨个执行这些回调是吧,外卖开店了赶紧给我定外卖,赶紧给他定奶茶。这个方法我们叫做trigger,也就是所谓触发这个事件。
const Observe = {
queue: {},
on(type, fn) {
if (typeof this.queue[type] === 'undefined') {
// 将执行方法推入该事件对应的执行队列中
this.queue[type] = [fn];
}
else {
// 如果此消息存在,直接将执行方法推入该事件对应的执行队列中
this.queue[type].push(fn);
}
},
trigger(type) {
if (!this.queue[type]) {
return;
}
this.queue[type].forEach(fn => {
fn();
})
}
}
这里我们的观察者其实已经可用了。我们实验一下:
看!我们已经成功的实现了我们的观察者,为自己点个赞吧!
留个作业,我们这里完成了一个最简单的观察者,但是这里有两个问题:
-
只有
订阅没有取消订阅,取消订阅的方法怎么写呢? -
当我们使用
Observe.queue的时候可以获取到这个事件的队列并且随意修改,这样显然是不安全的,请问怎样能让外部访问不了queue队列呢? -
本节课中我们为了讲述原理,将
观察者模式和订阅-发布模式放到一起讲了,但是实际上两者是有一定的差距的,大家怎么理解这个差距的?