# 观察者模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。
在观察者模式里,至少应该有两个关键角色是一定要出现的——发布者和订阅者。用面向对象的方式表达的话,那就是要有两个类。
发布者的类具备的基本功能:
- 增加订阅者
- 移除订阅者
- 通知订阅者
class Publisher {
constructor() {
this.observers = [];
console.log("Publisher is created");
}
add(observer) {
this.observers.push(observer);
console.log("add observer");
}
remove(observer) {
this.observers.forEach((item, index) => {
if (item === observer) {
this.observers.splice(index, 1);
}
});
console.log("remove observer");
}
notify() {
console.log("notify observer");
this.observers.forEach(item => {
item.update(this);
});
}
}
订阅者的功能
- 被通知
- 去执行 (本质上是接受发布者的调用,这步我们在
Publisher
中已经做了)
既然我们在 Publisher
中做的是方法调用,那么我们在订阅者类里要做的就是方法的定义
class Observer {
constructor() {
console.log("observer is created");
}
update(publish) {
console.log("observer is notified");
}
}
观察者模式经典面试题
Vue
数据双向绑定(响应式系统)的实现原理实现一个
Event Bus/ Event Emitter
function EventEmitter() {
// 用Object.create(null)创建空对象的方式与直接字面量方式{}的区别是:{}这种方式会有__proto__上面有很多属性
this._events = Object.create(null);
}
EventEmitter.prototype.on = function (eventName, callback) {
// (this._events[eventName] || []).push(callback)
// 如果实例上没有_events属性就添加上一个,指例子中的Myevents的情况 => 此时的this是Myevents的实例 而非 EventEmitter的实例 所以this上没有 _events
if (!this._events) this._events = Object.create(null);
// 如果当前的订阅不是newListener就执行 newListener的回调 并传递当前的事件名 用这种方式实现 监控on事件的触发
if (eventName !== "newListener") {
this.emit("newListener", eventName);
}
// 向对应事件的数组中添加callback
if (this._events[eventName]) {
this._events[eventName].push(callback);
} else {
this._events[eventName] = [callback];
}
};
EventEmitter.prototype.emit = function (eventName, ...args) {
if (this._events[eventName]) {
this._events[eventName].forEach(fn => {
fn(...args);
});
}
};
EventEmitter.prototype.once = function (eventName, callback) {
// 用one代替callback 在执行了callback之后 删除callback 由此实现了只执行一次
let one = () => {
callback();
this.off(eventName, one) // 下面on的是one所以这里off的应该也是one
}
// 如果手动off了 那么传入off的callback跟one肯定是不相等的 所以将callback赋值给one的自定义属性,用于在off中判断
one.l = callback
this.on(eventName, one)
}
EventEmitter.prototype.off = function (eventName, callback) {
if (this._events[eventName]) {
this._events[eventName] = this._events[eventName].filter(fn => {
// 返回false的会被过滤掉
return fn !== callback && fn.l !== callback
})
}
};
module.exports = EventEmitter;