当前位置:   article > 正文

手写EventHub发布订阅者模式_eventhub.on()

eventhub.on()

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

简单的发布与订阅

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

测试

import EventHub from "../src/index";
// on emit
let called = false;
eventHub.on("xxx", () => {
    called = true;
    console.log("called: ", called);
})

eventHub.emit("xxx");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

优化代码

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
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", "今天吃了泡面");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

第一版完整代码

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");

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

存在问题:执行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");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

在这里插入图片描述

解决办法

如果有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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

在这里插入图片描述

使用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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

测试

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 有用");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/100854
推荐阅读
相关标签
  

闽ICP备14008679号