观察者模式
- 在《javascript设计模式》这本书中, 在写到观察者模式的时候, 上来就第一句就是, 观察者模式又称发布订阅者模式.
- 说实话, 作为一个设计模式小白,我不太能分清楚他们到底是不是一种模式, 那就从先实现一个观察者模式开始.
- 在平时的开发过程中, 使用过
EventBus
,其实EventBus
就是一种观察者模式的实际应用.
初步实现
1. 观察者模式, 需要定一个装注册事件容器,这里使用一个对象, 然后, 需要订阅、发布、取消订阅的三个方法.
class Observer {
constructor() {
// 装订阅的容器
this.message = {}
}
// 注册消息。订阅消息
register(name, fn){}
// 发布消息
fire(name,...args){}
// 取消注册
remove(name,fn){}
}
- 上面定义了一个装事件的
message
和订阅消息的register
事件, 发布消息的fire
事件,取消订阅的remove
事件.
2. 实现register
注册/订阅方法
// 注册消息。订阅消息
register(name, fn){
// 如果message中没有需要注册的事件
if(!this.message.hasOwnProperty(name)){
// 初始化一个数组,并将这一次的事件放进去
// 使用数组是因为,一个事件名下面可能会注册多个fn方法
this.message[name] = [fn]
}else {
// 如果已经注册了,那么就将此次的方法push进去
this.message[name].push(fn)
}
// 返回this, 是因为可以进行链式调用
return this;
}
3. 实现fire
发布消息方法
// 发布消息
fire(name,...args){
// 获取注册事件下的方法数组
const fns = this.message[name]
console.log(fns)
// 如果没有对应的方法, 抛出异常
if(!fns) {throw new Error('该方法未被注册!')}
// 如果有对应的事件数组
// 遍历事件数组,并进行调用
fns.forEach(fn=>{
fn.call(this, ...args)
})
// 返回this, 是因为可以进行链式调用
return this;
}
4. 实现remove
移除订阅方法
// 取消注册
remove(name,fn){
if(!this.message.hasOwnProperty(name)){
return new Error('无该注册方法!')
}
// 下面两种方式都是可以的
// 方法一:forEach
// this.message.forEach((item, index) =>{
// if(item === fn) {
// this.message.splice(index, 1)
// }
// })
// 方法二:循环加&& 与运算
// 通过一次for循环
for (let i=0; i<fn.length; i++){
this.message[name][i] === fn && this.message[name].splice(i, 1)
}
// 返回this, 是因为可以进行链式调用
return this;
}
完整代码
class Observer {
constructor() {
this.message = {}
}
// 注册消息。订阅消息
register(name, fn){
if(!this.message.hasOwnProperty(name)){
this.message[name] = [fn]
}else {
this.message[name].push(fn)
}
return this;
}
// 发布消息
fire(name,...args){
const fns = this.message[name]
console.log(fns)
if(!fns) {throw new Error('该方法未被注册!')}
fns.forEach(fn=>{
fn.call(this, ...args)
})
return this;
}
// 取消注册
remove(name,fn){
if(!this.message.hasOwnProperty(name)){
return new Error('无该注册方法!')
}
// 下面两种方式都是可以的
// this.message.forEach((item, index) =>{
// if(item === fn) {
// this.message.splice(index, 1)
// }
// })
// 通过一次for循环
for (let i=0; i<fn.length; i++){
this.message[name][i] === fn && this.message[name].splice(i, 1)
}
return this;
}
}
测试代码
function add(a, b){
console.log(a + b )
}
function sub(a, b){
console.log(a - b)
}
const observer = new Observer()
observer.register('add',add) // 注册一个add方法, 返回 3
observer.register('sub',sub).fire('sub',1,2) // 注册并发布sub方法, 返回 -1
observer.fire('hoh',1) // 发布一个没有注册过的方法, 抛出‘Error: 该方法未被注册!’异常
致谢
参考文献《javascript设计模式》张容铭
感谢各位的查阅, 前端菜鸡如有理解错误请各位大佬指出, 让小弟也知道自己的错误, 感谢! 🙏