题目
interface IEmitter<Event> {
on(eventName: string, handler: (e: Event) => void): void;
once(eventName: string, handler: (e: Event) => void): void;
off(eventName: string, handler: (e: Event) => void): void;
emit(eventName: string, e: Event): void;
filter(fn: (e: Event) => boolean): IEmitter<Event>;
}
}
测试用例
describe('test events', () => {
it('用例1: on, emit, off 方法', function () {
let i = 0;
const emitter = new Emitter < number > ();
const handler = (num) => {
i += num;
};
emitter.on('add i', handler);
emitter.emit('add i', 2);
emitter.emit('add i', -1);
expect(i).toBe(1);
emitter.off('add i', handler);
emitter.emit('add i', 1);
expect(i).toBe(1);
});
it('用例2: once 方法', function () {
let i = 0;
const emitter = new Emitter < number > ();
const handler = (num) => {
i += num;
};
emitter.once('add i', handler);
emitter.emit('add i', 2);
emitter.emit('add i', -1);
expect(i).toBe(2);
});
}
作答
class Emitter<Event> implements IEmitter<Event> {
private listeners: Map<string, ((e: Event) => void)[]>;
constructor() {
this.listeners = new Map();
}
on(eventName: string, handler: (e: Event) => void): void {
if (!this.listeners.has(eventName)) {
this.listeners.set(eventName, []);
}
this.listeners.get(eventName)!.push(handler);
}
once(eventName: string, handler: (e: Event) => void): void {
const onceHandler = (e: Event) => {
handler(e);
this.off(eventName, onceHandler);
};
this.on(eventName, onceHandler);
}
off(eventName: string, handler: (e: Event) => void): void {
if (this.listeners.has(eventName)) {
const handlers = this.listeners.get(eventName)!;
const index = handlers.indexOf(handler);
if (index !== -1) {
handlers.splice(index, 1);
}
}
}
emit(eventName: string, e: Event): void {
if (this.listeners.has(eventName)) {
const handlers = this.listeners.get(eventName)!;
for (const handler of handlers) {
handler(e);
}
}
}
filter(fn: (e: Event) => boolean): IEmitter<Event> {
const filteredEmitter = new Emitter<Event>();
for (const [eventName, handlers] of this.listeners) {
const filteredHandlers = handlers.filter((handler) => fn);
for (const handler of filteredHandlers) {
filteredEmitter.on(eventName, handler);
}
}
return filteredEmitter;
}
}