手撕事件总线
基础版本
先写一个最基础的版本,可以实现事件的监听和触发,其中对于触发,我们需要考虑传参问题。
js
class EventBus {
constructor() {
this.eventObj = {};
}
on(eventName, callback) {
if (!this.eventObj[eventName]) {
this.eventObj[eventName] = [];
}
this.eventObj[eventName].push(callback);
}
emit(eventName, ...args) {
if (this.eventObj[eventName]) {
this.eventObj[eventName].forEach((callback) => {
callback(...args);
});
}
}
}
const eventBus = new EventBus();
eventBus.on("click", (data) => {
console.log("click", data);
});
eventBus.emit("click", "data1"); // click data1
eventBus.emit("click", "data2"); // click data2
多实例
接下来,我们考虑多实例的问题,这里就涉及到了事件的取消监听,并且这里很大一部分都要进行改动——因为对于每个实例都要有唯一标识。
js
class EventBus {
constructor() {
this.eventObj = {};
this.callbackId = 0;
}
on(eventName, callback) {
if (!this.eventObj[eventName]) {
this.eventObj[eventName] = {};
}
const id = this.callbackId++;
this.eventObj[eventName][id] = callback;
return id;
}
emit(eventName, ...args) {
if (this.eventObj[eventName]) {
const callbacks = this.eventObj[eventName];
for (const id in callbacks) {
callbacks[id](...args);
}
}
}
off(eventName, id) {
if (this.eventObj[eventName] && this.eventObj[eventName][id]) {
delete this.eventObj[eventName][id];
}
console.info(`off ${eventName} ${id}`);
if (!Object.keys(this.eventObj[eventName]).length) {
delete this.eventObj[eventName];
}
}
}
const eventBus = new EventBus();
const id1 = eventBus.on("click", (data) => {
console.log("click-id1", data);
});
const id2 = eventBus.on("click", (data) => {
console.log("click-id2", data);
});
eventBus.emit("click", "hello");
// click-id1 hello
// click-id2 hello
eventBus.off("click", id1);
eventBus.emit("click", "hello");
// off click 0
// click-id2 hello
销毁以及只执行一次
最后,再考虑一下事件总线的销毁和只执行一次的事件。
js
class EventBus {
// ...
emit(eventName, ...args) {
if (this.eventObj[eventName]) {
const callbacks = this.eventObj[eventName];
for (const id in callbacks) {
callbacks[id](...args);
if (id.indexOf("D" !== -1)) {
delete this.eventObj[eventName][id];
}
}
}
}
// ...
once(eventName, callback) {
if (!this.eventObj[eventName]) {
this.eventObj[eventName] = {};
}
const id = "D" + this.callbcakId++;
this.eventObj[eventName][id] = callback;
return id;
}
del(eventName) {
if (this.eventObj[eventName]) {
delete this.eventObj[eventName];
}
}
clear() {
this.eventObj = {};
}
}
总结
呈上最终完整的代码:
js
class EventBus {
constructor() {
this.eventObj = {};
this.callbackId = 0;
}
on(eventName, callback) {
if (!this.eventObj[eventName]) {
this.eventObj[eventName] = {};
}
const id = this.callbackId++;
this.eventObj[eventName][id] = callback;
return id;
}
emit(eventName, ...args) {
if (this.eventObj[eventName]) {
const callbacks = this.eventObj[eventName];
for (const id in callbacks) {
callbacks[id](...args);
if (id.indexOf("D") !== -1) {
delete this.eventObj[eventName][id];
}
}
}
}
off(eventName, id) {
if (this.eventObj[eventName] && this.eventObj[eventName][id]) {
delete this.eventObj[eventName][id];
}
console.info(`off ${eventName} ${id}`);
if (!Object.keys(this.eventObj[eventName]).length) {
delete this.eventObj[eventName];
}
}
once(eventName, callback) {
if (!this.eventObj[eventName]) {
this.eventObj[eventName] = {};
}
const id = "D" + this.callbackId++;
this.eventObj[eventName][id] = callback;
return id;
}
del(eventName) {
if (this.eventObj[eventName]) {
delete this.eventObj[eventName];
}
}
clear() {
this.eventObj = {};
}
getEvents() {
console.log(this.eventObj);
}
}
const bus = new EventBus();
const id1 = bus.on("click", (data) => {
console.log("click-id1", data);
});
const id2 = bus.on("click", (data) => {
console.log("click-id2", data);
});
const id3 = bus.once("double-click", (data) => {
console.log("double-click-id3", data);
});
bus.getEvents();
// {
// click: { '0': [Function (anonymous)], '1': [Function (anonymous)] },
// 'double-click': { D2: [Function (anonymous)] }
// }
bus.emit("click", "data1");
// click-id1 data1
// click-id2 data1
bus.emit("double-click", "data2");
// double-click-id3 data2
bus.getEvents();
// {
// click: { '0': [Function (anonymous)], '1': [Function (anonymous)] },
// 'double-click': {}
// }
bus.off("click", id1);
bus.getEvents();
// { click: { '1': [Function (anonymous)] }, 'double-click': {} }
bus.del("click");
bus.getEvents();
// { 'double-click': {} }
bus.clear();
bus.getEvents();
// {}