EventEmitter,普遍的应用于javascript语言中,浏览器事件(如鼠标单击click,键盘事件keyDown)都是该模式的例子。咱们在编写代码时也常常用它来解耦,好比:组件间咱们不想经过大量状态来通讯时,能够考虑用它来编写代码。不只如此,面试时可能面试官会让咱们实现一个EventEmitter,这时候可能有不少人就不会了。javascript
网上已经有不少EventEmitter的实现了,那我为啥还要写一个;首先,看一百次不如本身动手写一次;其次,这个模块很经常使用,本身定制的话也容易根据实际业务状况的变更作修改。java
以Nodejs 的 EventEmitter API为参考,咱们大概要实现如下的API:面试
首先咱们须要存储全部监听事件的地方,那么如何存储呢?常见的方法是使用哈希表,由于时间复杂度是 O(1),空间复杂度通常也不会太大。因此咱们的存储方式是:数组
interface EventType {
readonly callback: Function;
readonly once: boolean;
}
interface EventMap {
[propName: string]: EventType[]
}
export default class EventEmitter {
private eventMap: EventMap = {};
}
复制代码
接下来就是实现具体的方法浏览器
// 监听事件
on(event: string, callback: Function, once?: boolean) {
if (!this.eventMap[event]) {
this.eventMap[event] = [];
}
this.eventMap[event].push({
callback,
once: !!once,
});
return this;
}
// 监听事件一次
once(event: string, callback: Function) {
return this.on(event, callback, true);
}
复制代码
上面有两点须要注意下,首先,每一个事件的初始值是一个数组,由于对于一个事件,咱们可能会作多件事; 其次,监听事件一次,只是在调用监听事件方法时多加了一个参数,至于为何,咱们看看后面的触发事件方法就能明白。markdown
// 触发事件
emit(event: string, ...args: any[]) {
const events = this.eventMap[event] || [];
let length = events.length;
for (let i = 0; i < length; i++) {
if (!events[i]) {
continue;
}
const { callback, once } = events[i];
if (once) {
events.splice(i, 1);
if (events.length === 0) {
delete this.eventMap[event];
}
length--;
i--;
}
callback.apply(this, args);
}
}
复制代码
这里就对两种不一样状况下的事件监听作了不一样处理。app
// 取消事件监听
off(event?: string, callback?: Function) {
if(!event) {
// event 为空所有清除
this.eventMap = {}
} else {
if(!callback) {
// event 存在,但callback不存在
delete this.eventMap[event]
} else {
// event 存在,callback 存在,清除匹配的方法
const events = this.eventMap[event] || [];
let length = events.length;
for (let i = 0; i < length; i++) {
if (events[i].callback === callback) {
events.splice(i, 1);
length--;
i--;
}
}
if (events.length === 0) {
delete this.eventMap[event];
}
}
}
return this;
}
复制代码
这里的取消事件监听分了几种不一样状况,根据参数去判断。flex
interface EventType {
readonly callback: Function;
readonly once: boolean;
}
interface EventMap {
[propName: string]: EventType[]
}
export default class EventEmitter {
private eventMap: EventMap = {};
// 监听事件
on(event: string, callback: Function, once?: boolean) {
if (!this.eventMap[event]) {
this.eventMap[event] = [];
}
this.eventMap[event].push({
callback,
once: !!once,
});
return this;
}
// 监听事件一次
once(event: string, callback: Function) {
return this.on(event, callback, true);
}
// 触发事件
emit(event: string, ...args: any[]) {
const events = this.eventMap[event] || [];
let length = events.length;
for (let i = 0; i < length; i++) {
if (!events[i]) {
continue;
}
const { callback, once } = events[i];
if (once) {
events.splice(i, 1);
if (events.length === 0) {
delete this.eventMap[event];
}
length--;
i--;
}
callback.apply(this, args);
}
}
// 取消事件监听
off(event?: string, callback?: Function) {
if(!event) {
// event 为空所有清除
this.eventMap = {}
} else {
if(!callback) {
// event 存在,但callback不存在
delete this.eventMap[event]
} else {
// event 存在,callback 存在,清除匹配的方法
const events = this.eventMap[event] || [];
let length = events.length;
for (let i = 0; i < length; i++) {
if (events[i].callback === callback) {
events.splice(i, 1);
length--;
i--;
}
}
if (events.length === 0) {
delete this.eventMap[event];
}
}
}
return this;
}
// 获取当前全部的事件
getEvents() {
return this.eventMap;
}
}
复制代码
看完个人,但愿你们也能动手实现一个,本身写的才更不容易忘记。this