【前端面试题】实现一个EventEmitter

什么是EventEmitter?EventEmitter类似于Vue中的事件总线。有on、off、emit、once方法。接下来以Vue事件总线的方式进行解释,方便理解

首先,我们使用Class搭建起这个函数(普通函数也可以)

class EventEmitter {
    
    
    constructor() {
    
    
        // 初始化一个事件中心
        this.emitter = {
    
    };
        // 事件中心保存的内容是下面这样的,因为可以同时监听多次相同的事件
        // this.emitter = {
    
    
            // 'eventName1': [callback1, callback2, ...],
            // 'eventName2': [callback1, callback2, ...],
        // };
    }
}

首先实现on函数。on函数接收2个参数,第一个参数是事件名称,第二个参数的执行的回调函数。当我们使用emit派发事件时,on函数监听到就会执行相应的回调函数。

on(name, callback) {
    
    
    // 获取事件中心所存储的对应名称的事件
    const callbacks = this.emitter[name];
    // 如果保存过内容,就接着往里面添加,否则就当前callback就是第一个
    if (callbacks) {
    
    
        this.emitter[name].push(callback);
    } else {
    
    
        this.emitter[name] = [callback];
    }
}

监听的on函数有了,接下来实现派发事件的emit函数。emit函数接收2个参数,第一个参数是事件名称,第二个参数是要传递的参数。

emit(name, ...args) {
    
    
    // 获取事件中心所存储的对应名称的事件
    const callbacks = this.emitter[name];
    // 如果存在事件,就循环将监听的事件都触发,并且将参数传递过去
    if (callbacks) {
    
    
        callbacks.forEach(callback => {
    
    
            callback.apply(this, args);
        });
    }
}

如果觉得监听的事件过多想要删除掉怎么办,off函数出现了。off函数接收2个参数,第一个参数是事件名称,第二参数是要卸载的函数

off(name, fn) {
    
    
    // 获取事件中心所存储的对应名称的事件
    const callbacks = this.emitter[name];
    // 存在回调
    if (callbacks) {
    
    
        // 找到当前要卸载的函数
        const index = callbacks.findIndex(item => item === fn);
        // 从事件列表中删除
        this.emitter[name].splice(index, 1);
    }
}

最后是只触发一次的onceonce函数接收2个参数,第一个参数是事件名称,第二个参数的执行的回调。

once(name, callback) {
    
    
    // 2、在绑定事件中执行并卸载,到达只执行一次的效果
    const fn = (...args) {
    
    
        fn.apply(this, args);
        this.off(name, fn);
    }
    // 1、绑定事件
    this.on(name, fn);
}

完整代码:

class EventEmitter {
    
    
    constructor() {
    
    
        this.emitter = {
    
    };
    }
    on(name, callback) {
    
    
        const callbacks = this.emitter[name];
        if (callbacks) {
    
    
            this.emitter[name].push(callback);
        } else {
    
    
            this.emitter[name] = [callback];
        }
    }
    emit(name, ...args) {
    
    
        const callbacks = this.emitter[name];
        if (callbacks) {
    
    
            callbacks.forEach(callback => {
    
    
                callback.apply(this, args);
            });
        }
    }
    off(name, fn) {
    
    
        const callbacks = this.emitter[name];
        if (callbacks) {
    
    
            const index = callbacks.findIndex(item => item === fn);
            this.emitter[name].splice(index, 1);
        }
    }
    once(name, callback) {
    
    
        const fn = (...args) => {
    
    
            fn.apply(this, args);
            this.off(name, fn);
        }
        this.on(name, fn);
    }
}

测试:

const e = new Emitter();
// on
e.on('a', (...args) => {
    
     // fn1
    console.log('a', ...args);
});
e.on('a', (...args) => {
    
     // fn2
    console.log('a', ...args);
});
e.on('b', (..args) => {
    
     // fn3
    console.log('b', ...args);
});
// 此时emitter中保存的是 this.emitter = { a: [fn1, fn2], b: [fn3] }


// emit
e.emit('a', 'a1', 'a2', 'a3', 'a4'); // 输出2次 a1、a2、a3、a4
e.emit('b', 'b'); // 输出一次 b

// off
const fn = () => {
    
    
    console.log('off fn');
};
e.on('b', fn);
e.emit('b'); // 触发输出2次内容,分别为b和off fn
e.off('b', fn); // 卸载
e.emit('b'); // 再次触发,只输出b

// once
e.once('c', () => {
    
    
    console.log(e.emitter); // this.emitter = { c: [fn] }
    console.log('once');
});

e.emit('c', () => {
    
    
    console.log(e.emitter); // this.emitter = { c: [] }
});

猜你喜欢

转载自blog.csdn.net/qq_43398736/article/details/126861888