手动实现一个eventEmitter
观察者模式在前端开发中非常常用,我们经常用的事件就是观察者模式的一种体现。它对我们解耦模块、开发基于消息的业务起着非常重要的作用。Node.js 原生自带 EventEmitter 模块,可见它的重要性。
作为在工作中经常遇到和面试的经典题目当然要琢磨透彻,下面一步步手动写一个eventEmitter
首先,我们要知道EE的api是什么样的
node的EventEmitter包含了很多常用的API,我们一一来介绍几个实用的API.
方法名
|
方法描述
|
addListener(event, listener)
|
为指定事件添加一个监听器到监听器数组的尾部。
|
prependListener(event,listener)
|
与addListener相对,为指定事件添加一个监听器到监听器数组的头部。
|
on(event, listener)
|
其实就是addListener的别名
|
once(event, listener)
|
为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
|
removeListener(event, listener)
|
移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器
|
off(event, listener)
|
removeListener的别名
|
removeAllListeners([event])
|
移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
|
setMaxListeners(n)
|
默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。
|
listeners(event)
|
返回指定事件的监听器数组。
|
emit(event, [arg1], [arg2], [...])
|
按参数的顺序执行每个监听器,如果事件有注册监听返回 true,否则返回 false。
|
现在,我们来主要实现on,emit,removeListener 等几个api
每一个EventEmitter实例都有一个包含所有事件的对象_events,
事件的监听和监听事件的触发,以及监听事件的移除等都在这个对象_events的基础上实现。
首先实现一个EventEmitter类
class EventEmitter {
constructor() {
this._events = Object.create(null); // 定义事件的存储对象
this._eventsCount = 0;
}
}
接着实现添加事件监听的on方法:
-
为每个事件名称添加一个数组作为事件的存储器
-
事件由一个对象来保存,为了区别开on和once需要为每个事件增加一个isOnce字段
//添加事件监听
on(eventName, fn, isOnce = false) {
if (typeof fn !== "function") {
throw new TypeError("The listener must be a function!");
}
if (!this._events[eventName]) {
this._events[eventName] = [];
this._events[eventName].push({ fn, isOnce });
} else {
this._events[eventName].push({ fn, isOnce }); // 存入监听的事件名和事件
}
}
实现事件触发方法emit:
-
找到事件名称下面的所有事件以此触发
-
当事件的isOnce 为true的时候在触发事件之后清空事件
// 事件触发
emit(eventName, ...args) {
if (!this._events[eventName]) {
return false;
}
const len = this._events[eventName].length;
for (let i = 0; i < len; i++) {
let event = this._events[eventName][i];
event.fn.call(this, ...args);
if (event.isOnce) {
this.removeListener(eventName, event.fn);
i--;
}
}
}
事件监听解绑
1.清空事件名下的监听的函数,函数为undefined则清空事件名下所有事件
// 移除监听事件
removeListener(eventName, fn) {
if (!this._events[eventName]) return this;
if (!fn) {
delete this._events[eventName];
return this;
} else {
this._events[eventName].forEach((item, index) => {
if (item.fn === fn) {
this._events[eventName].splice(index, 1);
} else {
return this;
}
});
}
}
全部代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>手动实现一个eventEmitter</title>
</head>
<body>
<script>
class EventEmitter {
constructor() {
this._events = Object.create(null); // 定义事件的存储对象
this._eventsCount = 0;
}
//添加事件监听
on(eventName, fn, isOnce = false) {
if (typeof fn !== "function") {
throw new TypeError("The listener must be a function!");
}
if (!this._events[eventName]) {
this._events[eventName] = [];
this._events[eventName].push({ fn, isOnce });
} else {
this._events[eventName].push({ fn, isOnce }); // 存入监听的事件名和事件
}
}
//一次性事件监听
once(eventName, fn) {
this.on(eventName, fn, true);
}
// 事件触发
emit(eventName, ...args) {
if (!this._events[eventName]) {
return false;
}
const len = this._events[eventName].length;
for (let i = 0; i < len; i++) {
let event = this._events[eventName][i];
event.fn.call(this, ...args);
if (event.isOnce) {
this.removeListener(eventName, event.fn);
i--;
}
}
}
// 移除监听事件
removeListener(eventName, fn) {
if (!this._events[eventName]) return this;
if (!fn) {
delete this._events[eventName];
return this;
} else {
this._events[eventName].forEach((item, index) => {
if (item.fn === fn) {
this._events[eventName].splice(index, 1);
} else {
return this;
}
});
}
}
// off:removeListener 的别名
off(eventName, fn) {
this.removeListener(eventName, fn);
}
// 移除所有监听事件
removeAllListener(eventName) {
if (eventName) {
if (this._events[eventName]) {
this._events[eventName].length = 0;
}
} else {
this._events = Object.create(null);
}
}
}
function add(...args) {
let num = 0;
for (let i = 0; i < args.length; i++) {
num += args[i];
}
console.log(num);
return num;
}
let event = new EventEmitter();
// event.on("adding", add);
// event.emit("adding", 1, 2, 3, 4);
// event.removeListener("adding", add);
// event.emit("adding", 1, 2, 3, 4);
// event.once("adding", add);
event.emit("adding", 1, 2, 3, 4);
event.emit("adding", 1, 2, 3, 4);
</script>
</body>
</html>