手写EventHub发布订阅者模式

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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值