观察者模式
观察者模式又叫发布-订阅模式,是我们常见的设计模式(其实二者有所区别,具体可baidu), 它定义对象间的一种一对多的依赖关系, 当一个对象的状态发生改变时, 所有依赖于它的对象都将得到通知。观察者模式提供了一个订阅模型,其中对象订阅事件并在发生时得到通知,这种模式是事件驱动的编程基石,它有利益于良好的面向对象的设计。
可能我们对该模式的理解还不够清晰,举个🌰:
小米最近看上了一套房子,但是去购买时发现房子已出售,售楼小姐告诉他过一阵子还有新的楼盘出售,然后小米就在之后每天都给售楼小姐打电话询问,我们一定会觉得这种行为很🤷♂️,所以实际上是这样的:在小米离开售楼部时,留下了自己的联系方式,当楼盘开始出售时,售楼小姐发短信通知她。在这个例子中,小米订阅了售楼部的消息,作为订阅者;售楼小姐发送通知,作为发布者,这就是生活中的发布订阅模式。
代码实现:
let SalesOffice = {
arr: [], //花名册
reception: function(fn) { //将客户的电话存入花名册
this.arr.push(fn);
},
sendMsg: function() {
for (let i = 0, list = this.arr, fn; fn = list[i++];) {
fn.apply(this, arguments);
}
}
}
SalesOffice.reception(function(size, money) {
console.log(size + '平方' + '\n' + money + '元');
})
SalesOffice.sendMsg(80, 5000000);
使用场景
一、DOM操作
只要我们曾经在DOM节点上面绑定过事件函数,那我们就使用过观察者模式,应为JS和DOM之间就是实现了一种观察者模式。
document.body.addEventListener("click", function() {
alert("Hello")
},false )
document.body.click() //用户点击
在上面的代码中,我们监听用户点击 document.body 的动作,但是我们是没办法预知用户将在什么时候点击的。因此我们订阅了 document.body 的 click 事件,当 body 节点被点击时,body 节点便会向订阅者发布 “Hello” 消息。
二、自定义事件
很多时候,内置事件无法满足我们的需求,于是需要我们自定义事件,举个🌰:
const event = {
clientList: {},//事件列表
listen: function(key, fn) {//监听事件函数
if (!this.clientList[key]) {
this.clientList[key] = []
}
this.clientList[key].push(fn)//将回调函数推入对象的键对应的值
},
trigger: function() {//触发事件函数
const key = Array.prototype.shift.call(arguments)
const fns = this.clientList[key]
if (!fns || fns.length === 0 ) {
return false//如果回调数组不存在或为空则返回false
}
for (let i = 0, fn ;fn = fns[i++];) {
fn.apply(this, arguments)//循环回调数组执行回调函数
}
},
remove : function(key , fn) {//移除事件函数
const fns = this.clientList[key]
if (!fns) {
return false//事件不存在直接返回false
}
if (!fn) {
delete list[key]; //如果没有后续参数,则删除整个回调数组
} else {
for (let l = fns.length - 1; l>=0; l--) {
const _fn = fns[l]
if ( _fn === fn) {
fns.splice(l, 1)//删除特定回调数组中的回调函数
}
}
}
}
const installEvent = obj => {
for (let i in event) {
obj[i] = event[i]
}
}
添加发布订阅功能
const events = {}
installEvent(events)
// 订阅信息
events.listen('newMessage', fn1 = say => {
console.log('say:' + say)
})
// 发布信息
events.trigger('newMessage', "Hello")
//移除订阅
events.remove('newMessage', fn1)
总结
观察者模式有两个明显的优点
时间上解耦
对象间解耦
既可用于异步编程中,也可以用帮助我们完成更松耦合的代码编写。
它应用广泛,但是也有缺点,创建订阅者本身要消耗一定的时间和内存;观察者模式弱化了对象之间的联系,这本是好事情,但如果过度使用,对象与对象之间的联系也会被隐藏的很深,会导致项目的难以跟踪维护和理解。