# 发布订阅者模式(观察者模式)
什么是发布-订阅者模式
又叫观察者模式,它定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知
在 JS 开发中,我们一般用事件模型来替代传统的发布-订阅者模式。
# 核心思想
核心
取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的某个接口
在 JS 中通常使用注册回调函数的形式来订阅。
# 具体实现
JS 中的事件就是经典的发布-订阅模式的实现
// 订阅
document.body.addEventListener(
"click",
function () {
console.log("click1");
},
false
);
document.body.addEventListener(
"click",
function () {
console.log("click2");
},
false
);
// 发布
document.body.click(); // click1 click2
举例
小 A 在公司 C 完成了笔试及面试,小 B 也在公司 C 完成了笔试。他们焦急地等待结果,每隔半天就电话询问公司 C,导致公司 C 很不耐烦。
一种解决办法是 AB 直接把联系方式留给 C,有结果的话 C 自然会通知 AB。这里的“询问”属于显示调用,“留给”属于订阅,“通知”属于发布。
// 观察者
var observer = {
// 订阅集合
subscribes: [],
// 订阅
subscribe: function (type, fn) {
if (!this.subscribes[type]) {
this.subscribes[type] = [];
}
// 收集订阅者的处理
typeof fn === "function" && this.subscribes[type].push(fn);
},
// 发布 可能会携带一些信息发布出去
publish: function () {
var type = [].shift.call(arguments),
fns = this.subscribes[type];
// 不存在的订阅类型,以及订阅时未传入处理回调的
if (!fns || !fns.length) {
return;
}
// 挨个处理调用
for (var i = 0; i < fns.length; ++i) {
fns[i].apply(this, arguments);
}
},
// 删除订阅
remove: function (type, fn) {
// 删除全部
if (typeof type === "undefined") {
this.subscribes = [];
return;
}
var fns = this.subscribes[type];
// 不存在的订阅类型,以及订阅时未传入处理回调的
if (!fns || !fns.length) {
return;
}
if (typeof fn === "undefined") {
fns.length = 0;
return;
}
// 挨个处理删除
for (var i = 0; i < fns.length; ++i) {
if (fns[i] === fn) {
fns.splice(i, 1);
}
}
},
};
// 订阅岗位列表
function jobListForA(jobs) {
console.log("A", jobs);
}
function jobListForB(jobs) {
console.log("B", jobs);
}
// A订阅了岗位
observer.subscribe("job", jobListForA);
// B订阅了岗位
observer.subscribe("job", jobListForB);
// A订阅了笔试成绩
observer.subscribe("examinationA", function (score) {
console.log(score);
});
// B订阅了笔试成绩
observer.subscribe("examinationB", function (score) {
console.log(score);
});
// A订阅了面试结果
observer.subscribe("interviewA", function (result) {
console.log(result);
});
observer.publish("examinationA", 100); // 100
observer.publish("examinationB", 80); // 80
observer.publish("interviewA", "备用"); // 备用
observer.publish("job", ["前端", "后端", "测试"]); // 输出A和B的岗位
// B取消订阅了笔试成绩
observer.remove("examinationB");
// A取消订阅了岗位
observer.remove("job", jobListForA);
observer.publish("examinationB", 80); // 没有可匹配的订阅,无输出
observer.publish("job", ["前端", "后端", "测试"]); // 输出B的岗位
# 优缺点
优点
一为时间上的解耦,二为对象之间的解耦,可以用在异步编程中。
缺点
创建订阅者本身要消耗一定的时间和内存,订阅的处理函数不一定会被执行,驻留内存有性能开销,弱化了对象之间的联系,复杂的情况下可能会导致程序难以跟踪维护和理解。
大厂最新技术学习分享群
微信扫一扫进群,获取资料
X