在函数式编程中,观察者设计模式是非常有必要的,尽管在JS里到处都充斥着观察者模式(Observer Pattern),但我们仍然有必要去了解它。以便实现自己的观察者模式,用于更复杂的应用场景。
其实要实现观察者模式实在是很简单。
现在我们回顾一下我们遇到过的观察者模式,最常见的应该是在前端领域中的DOM事件监听。
JS允许我们用两种形式绑定DOM事件,一种为钩子捕获,另一种为事件监听,今天我们主要的关注点在后者,如果还不了解钩子函数和回调函数的朋友可以去看一下我写的另一篇博文钩子函数与回调函数的区别
下面来看一个简单的DOM事件的监听
//我们假设btn 就是一个button DOM对象的引用
btn.on('click',() => {
console.log('我被点击了');
});
这就是一个简单的观察者模式的例子,相信大家对这种监听机制早就已经看的快烂了,写起来也是特别顺手,它其实是当用户鼠标点击到这个DOM元素的时候,由btn对象触发自己的click事件,然后调用相应的回调函数,整个操作是异步的。
下面我们来实现自己的观察者模式
let Producter = function(){
this.listeners = [];
}
//添加listener
Producter.prototype.add = function(listener){
this.listeners.push(listener);
}
//删除listener
Producter.prototype.remove = function(listener){
let index = this.listeners.indexOf(listener);
this.listeners.splice(index,1);
}
//发布者做出更改
Producter.prototype.notify = function(msg){
for(let listener of this.listeners){
listener.update(msg);
}
}
//定义两个listener
let listener1 = {
update:msg => {
console.log(`callback listener1 ${msg}`);
}
};
let producter = new Producter();
producter.add(listener1);
producter.notify("hello world");
productor.add
这个方法主要用于添加观察者(也叫订阅者)对象,其实在被观察者(也叫发布者)有一个Array,用于存储观察者对象,当事件发生改变时,就依次调用观察者相应的函数。
productor.notify
这个方法主要是通知所有的观察者:“我已经做出改变,请执行你们各自的处理函数”
当然,这里的操作并不是异步的,不过我们也可以模拟一下异步场景,比如我们更改一下productor对象的notify的调用上下文。
setTimeout(() => {
producter.notify("hello world");
},1000);
在它的外层嵌套上一层setTimeout,它会被加入事件循环,等待执行栈空闲的时候会执行它。
你可能对这样的实现方式并不感冒,因为它和之前介绍的绑定DOM事件的方式并不是很相同,为此,我们下面来实现一个相似的场景,即事件的订阅与发布。
为了方便演示,这里我不再实现删除观察者的方法
下面我贴出代码
//观察者
let Obj = function(){
this.listeners = [];
}
Obj.prototype = {
//订阅事件
on:function(eventName,Fn){
if(!this.listeners[eventName]){
this.listeners[eventName] = Fn;
}
},
//触发事件 (这里的argum是要触发时传入给回调函数的参数,它为一个数组)
trigger:function(eventName,...argum){
this.listeners[eventName](argum);
}
};
let obj = new Obj();
obj.on("click",msg => {
console.log(msg);
});
obj.trigger("click","hello world");
这样是不是看起来更加友好一些,事实上,作为一名Front-End Engineer,跟我们每天打交道的观察者模式就是类似于事件的发布与订阅的这种形式。