发布 — 订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,全部依赖于它的对象都将获得通知。缓存
在 JavaScript开发中,咱们通常用事件模型来替代传统的发布 — 订阅模式。bash
下面来模拟下EventEmitter的初步实现app
class EventEmitter {
constructor() {
this._events = {};//用对象的方式来缓存订阅者队列(事件名称:回调)
}
on(eventName, listener) {
if(typeof listener !== 'function') { return; }
if(!this._events) {//若是只被继承了prototype,须要在继承的对象上添加_events属性
this._events = Object.create(null);
}
if(!this._events[eventName]) {//事件队列不存在
this._events[eventName] = [];
}
this._events[eventName].push(listener);//添加观察者
}
addListener(eventName, listener) {
this.on(eventName, listener);
}
removeListener(eventName, listener) {
if(!this._events[eventName]) { return; }
this._events[eventName] = this._events[eventName].forEach(item => {
return item !== listener;
});
}
emmit(eventName, ...args) {//状态改变
if(!this._events[eventName]) { return; }
this._events[eventName].forEach(callback => {//通知全部的订阅者,发起回调
callback.apply(this, args);
});
}
}
复制代码
EventEmitter中的once方法能够作到绑定的事件只调用一次,以后不会再被调用,他的实现方式实在怎么样的?正常状况应该是在回调函数被调用一次以后移除这个回调。能够考虑在回调函数上加上once属性,在发起回调的时候判断once是否为真,来肯定是否移除这个回调。这样能够达到目的,可是在发起回调时,须要每一次都判断,给通知方法增长了额外的负担,来考虑一个更聪明的实现方式。函数
once(eventName, listener) {
function wrap(args) {
listener.apply(this, args);
this.removeListener(eventName, wrap);
}
wrap.cb = listener;//将回调存储起来用于删除时对比
this.on(eventName, wrap);
}
复制代码
将回调函数包裹起来,在包裹函数内部移除原回调函数,而后将wrap函数添加进观察者队列。同时要将原回调函数存进wrap中,用在在移除原回调时判断。ui
removeListener(eventName, listener) {
if(!this._events[eventName]) { return; }
this._events[eventName] = this._events[eventName].forEach(item => {
return item !== listener && item.cb !== listener;
});
}
复制代码
比较有趣的是EventEmitter同时提供了newListener事件,每次添加观察者(即便是第二次添加newListener)时都会触发这个事件,在on方法中须要添加以下代码:this
this.emmit('newListener', eventName, listener);//触发newListener事件回调
复制代码
这个静态属性限制了一种事件能够添加的最大回调数量,同时还有配套的setMaxListeners和getMaxListeners方法来设置和获取每一个事件能够添加的最大回调数量spa
setMaxListeners(n) {
this.maxListeners = n;
}
getMaxListeners() {
return this.maxListeners ? this.maxListeners : EventEmitter.defaultMaxListeners;
}
复制代码
on方法添加判断;prototype
if(this._events[eventName].length > this.getMaxListeners()){
console.warn('超过最大数量,请修改maxListeners')
}
复制代码
class EventEmitter {
constructor() {
this._events = {};//用对象的方式来缓存订阅者队列(事件名称:回调)
}
setMaxListeners(n) {
this.maxListeners = n;
}
getMaxListeners() {
return this.maxListeners ? this.maxListeners : EventEmitter.defaultMaxListeners;
}
on(eventName, listener) {
if(typeof listener !== 'function') { return; }
if(!this._events) {//若是只被继承了prototype,须要在继承的对象上添加_events属性
this._events = Object.create(null);
}
this.emmit('newListener', eventName, listener);//触发newListener事件回调
if(!this._events[eventName]) {//事件队列不存在
this._events[eventName] = [];
}
this._events[eventName].push(listener);//添加观察者
if(this._events[eventName].length > this.getMaxListeners()){
console.warn('超过最大数量,请修改maxListeners')
}
}
once(eventName, listener) {
function wrap(args) {
listener.apply(this, args);
this.removeListener(eventName, wrap);
}
wrap.cb = listener;//将回调存储起来用于删除时对比
this.on(eventName, wrap);
}
addListener(eventName, listener) {
this.on(eventName, listener);
}
removeListener(eventName, listener) {
if(!this._events[eventName]) { return; }
this._events[eventName] = this._events[eventName].forEach(item => {
return item !== listener && item.cb !== listener;
});
}
emmit(eventName, ...args) {//状态改变
if(!this._events[eventName]) { return; }
this._events[eventName].forEach(callback => {//通知全部的订阅者,发起回调
callback.apply(this, args);
});
}
}
复制代码