手写EventHub发布订阅者模式
文章目录EventHub确定API优化代码emit传可选参数off 取消事件订阅完整代码EventHubEventHub是个全局对象,用于多个组件之间的通讯就是发布订阅模式比如:2.js注册事件on(“xxx”)1.js触发事件emit(“xxx”)确定APIEventHub#onEventHub#offEventHub#emit测试import EventHub from "../src/ind
·
EventHub
EventHub是个全局对象,用于多个组件之间的通讯
就是发布订阅模式
比如:
EventHub.on
注册事件on(“xxx”, fn)EventHub.emit
触发事件emit(“xxx”)EventHub.off
删除事件off(“xxx”, fn)
搭建主体
类实现
class EventHub{
on(eventName, fn){};
emit(eventName){};
}
export default EventHub;
简单的发布与订阅
class EventHub {
/*
{
"楚天都市报": [fn1, fn2, fn3, fn4] ,
"羊城晚报": [fn1, fn2, fn3]
}
*/
cache = {};
on(eventName, fn) {
if (this.cache[eventName] === undefined) {
// 初始化事件名
this.cache[eventName] = [];
}
const arry = this.cache[eventName];
arry.push(fn);
}
emit(eventName) {
let arr = this.cache[eventName];
if (arr === undefined) {
arr = [];
}
arr.forEach(fn => {
fn();
});
}
}
export default EventHub;
测试
import EventHub from "../src/index";
// on emit
let called = false;
eventHub.on("xxx", () => {
called = true;
console.log("called: ", called);
})
eventHub.emit("xxx");
优化代码
class EventHub {
cache = {};
on(eventName, fn) {
// api on 把函数推进cache数组
this.cache[eventName] = this.cache[eventName] || [];
this.cache[eventName].push(fn);
}
emit(eventName) {
// emit 把cache里面的fn依次调用
(this.cache[eventName] || []).forEach(fn => fn());
}
}
export default EventHub;
emit传可选参数
class EventHub {
cache = {};
on(eventName, fn) {
// api on 把函数推进cache数组
this.cache[eventName] = this.cache[eventName] || [];
this.cache[eventName].push(fn);
}
emit(eventName, data) {
// emit 把cache里面的fn依次调用
(this.cache[eventName] || []).forEach(fn => fn(data));
}
}
export default EventHub;
import EventHub from "../src/index";
const eventHub = new EventHub();
// on emit
let called = false;
eventHub.on("xxx", (data?) => {
called = true;
console.log("called: ", called);
console.log("data: ", data === "今天吃了泡面");
})
eventHub.emit("xxx", "今天吃了泡面");
off 取消事件订阅
class EventHub {
...
off(eventName, fn) {
// 删除cache中的事件fn
let index = indexOf(this.cache[eventName], fn);
if (index === -1) return;
this.cache[eventName].splice(index, 1);
}
}
function indexOf(arr, item) {
if (arr === undefined ) return -1;
let index = -1;
for (let i = 0; i < arr.length; i++) {
if (arr[i] === item) {
index = i;
break;
}
}
return index;
}
第一版完整代码
function indexOf(arr, item) {
if (arr === undefined) return -1;
let index = -1;
for (let i = 0; i < arr.length; i++) {
if (arr[i] === item) {
index = i;
break;
}
}
return index;
}
class EventHub {
cache = {};
on(eventName, fn) {
// api on 把函数推进cache数组
this.cache[eventName] = this.cache[eventName] || [];
this.cache[eventName].push(fn);
}
emit(eventName, data) {
// emit 把cache里面的fn依次调用
(this.cache[eventName] || []).forEach(fn => fn(data));
}
off(eventName, fn) {
// 删除cache中的事件fn
let index = indexOf(this.cache[eventName], fn);
if (index === -1) return;
this.cache[eventName].splice(index, 1);
}
}
const eventHub = new EventHub();
function fn1() {
console.log(1);
}
function fn2() {
console.log(2);
}
function fn3() {
console.log(3);
eventHub.off("init", fn1);
eventHub.off("init", fn2);
eventHub.off("init", fn3);
}
function fn4() {
console.log(4);
}
function fn5() {
console.log(5);
}
function fn6() {
console.log(6);
}
eventHub.on("init", fn1);
eventHub.on("init", fn2);
eventHub.on("init", fn3);
eventHub.on("init", fn4);
eventHub.on("init", fn5);
eventHub.on("init", fn6);
eventHub.emit("init");
eventHub.emit("init");
存在问题:执行off产生数组塌陷
因为每次都是使用splice来切割数组的,所以off之后的事件直接消失,后面的事件就往前补空缺,使得下标index改变
const eventHub = new EventHub();
function fn1() {
console.log(1);
}
function fn2() {
console.log(2);
}
function fn3() {
console.log(3);
eventHub.off("init", fn1);
eventHub.off("init", fn2);
eventHub.off("init", fn3);
}
function fn4() {
console.log(4);
}
function fn5() {
console.log(5);
}
function fn6() {
console.log(6);
}
eventHub.on("init", fn1);
eventHub.on("init", fn2);
eventHub.on("init", fn3);
eventHub.on("init", fn4);
eventHub.on("init", fn5);
eventHub.on("init", fn6);
console.log("第一次执行事件");
eventHub.emit("init");
console.log("第二次执行事件");
eventHub.emit("init");
解决办法
如果有off取消事件,就先将off的事件指向null
在下一次emit触发事件的时候再重构事件数组
最终版本
function indexOf(arr, item) {
if (arr === undefined) return -1;
let index = -1;
for (let i = 0; i < arr.length; i++) {
if (arr[i] === item) {
index = i;
break;
}
}
return index;
}
class EventHub {
cache = {};
on(eventName, fn) {
// api on 把函数推进cache数组
this.cache[eventName] = this.cache[eventName] || [];
this.cache[eventName].push(fn);
}
emit(eventName, data) {
// console.log(this.cache[eventName]);
// 删除数组中的无效事件
this.cache[eventName] = this.cache[eventName].filter(fn => fn != null);
// console.log(this.cache[eventName]);
// emit 把cache里面的fn依次调用
(this.cache[eventName] || []).forEach(fn => fn && fn(data));
}
off(eventName, fn) {
// 删除cache中的事件fn
let index = indexOf(this.cache[eventName], fn);
if (index === -1) return;
this.cache[eventName][index] = null;
}
}
使用typescript重构代码
class EventHub {
private cache: { [key: string]: Array<(data: unknown) => void> } = {};
// 订阅
on(eventName: string, fn: (data: unknown) => void) {
// api on 把函数推进cache数组
this.cache[eventName] = this.cache[eventName] || [];
this.cache[eventName].push(fn);
}
// 发布
emit(eventName: string, data?: unknown) {
// emit 把cache里面的fn依次调用
(this.cache[eventName] || []).forEach(fn => fn(data));
}
remove(eventName: string, fn: (data: unknown) => void) {
// 删除cache中的事件fn
let index = indexOf(this.cache[eventName], fn);
if (index === -1) return;
this.cache[eventName].splice(index, 1);
}
}
function indexOf(arr, item) {
if (arr === undefined) return -1;
let index = -1;
for (let i = 0; i < arr.length; i++) {
if (arr[i] === item) {
index = i;
break;
}
}
return index;
}
export default EventHub;
测试
import EventHub from "../src/index";
type TestCase = (message: string) => void;
const test1: TestCase = message => {
const eventHub = new EventHub();
console.assert(eventHub instanceof Object === true, "eventHub 是个对象");
console.log(message);
};
const test2: TestCase = message => {
const eventHub = new EventHub();
// on emit
let called = false;
eventHub.on("xxx", y => {
called = true;
console.assert(y[0] === "点了份外卖");
console.assert(y[1] === "晚上吃了泡面");
});
eventHub.emit("xxx", ["点了份外卖", "晚上吃了泡面"]);
console.assert(called);
console.log(message);
};
const test3: TestCase = message => {
const eventHub = new EventHub();
let called = false;
const fn1 = () => {
called = true;
};
eventHub.on("yyy", fn1);
eventHub.remove("yyy", fn1);
eventHub.emit("yyy");
console.assert(called === false);
console.log(message);
};
test1("EventHub 可以创建对象");
test2(".on 了之后 .emit,会触发 .on 的函数");
test3(".off 有用");
更多推荐
所有评论(0)