【场景应用题】实现订阅发布

204 阅读1分钟

发布订阅模式是我们在项目中常见的模式,也是面试中常考的手写题。

代码实现如下:

// 实现发布订阅模式
class EventEmitter {
    // 1.定义事件容器,用来装事件数组
    handlers = {}

    // 2.添加事件方法,参数:事件名 事件方法 是否只执行一次
    on(type, handler, once = false) {
        if (!this.handlers[type]) {
            this.handlers[type] = [];
        }

        if (!this.handlers[type].includes(handler)) {
            this.handlers[type].push(handler);
            handler.once = once;
        }
    }
    // 3.触发事件方法,参数:事件名 事件方法
    trigger(type, params) {
        if (this.handlers[type]) {
            this.handlers[type].forEach(handler => {
                handler.call(this, params);
                if (handler.once) {
                    this.off(type, handler)
                }
            });
        } else {
            return new Error('该事件未注册')
        }
    }
    // 4.移除事件方法,参数:事件名 事件方法,若无第二个参数则删除该事件的订阅发布
    off(type, handler) {
        // 若没有注册该事件,则抛出错误
        if (!this.handlers[type]) {
            return new Error('事件无效')
        }
        // 移除事件
        if (!handler) {
            delete this.handlers[type]
        } else {
            this.handlers[type] = this.handlers[type].filter(h => h !== handler)
            if (this.handlers[type].length === 0) {
                delete this.handlers[type]
            }
        }
    }
    // 5.只执行一次事件方法,参数:事件名 事件方法,执行一次后自动删除
    once(type, handler) {
        this.on(type, handler, true)
    }
}

const ev = new EventEmitter();

function handler1(a) {
    console.log('handler1', ...a);
}
function handler2(a) {
    console.log('handler2', ...a);
}
function handler3(a) {
    console.log('handler3', ...a);
}

ev.on('test', handler1);
ev.on('test', handler2);
ev.on('test', handler3);

ev.trigger('test', [123, 456])