first step is to think about the data structure. The most straightforward choice is to use Map, because we need to use a <K,V> for storage
here terminal will output with:
now, the first issue comes, when we use Map<event,Function> as the data structure then we will face how to trigger the same event with different callback function… problems like below.
the second log will overlap the first one,because if there is existed key in Map, then set() will update with the value and overlap the previous valu. so Map<K,V>should use -> Map<string, Function[]> instead of Map<string, Function>
console.log("*".repeat(40), "version 3"); classEventEmitterV4 { privateeventRegister: Map<string, { fn: Function, onceFlag: boolean }[]>;//we need to update the structure constructor() { this.eventRegister = newMap(); }
on(event: string, fn: Function, onceFlag: boolean = false) { const fnList = this.eventRegister.get(event); if (fnList) fnList.push({ fn, onceFlag }); // need to baesed on the structure elsethis.eventRegister.set(event, [{ fn, onceFlag }]);// need to baesed on the structure }
once(event: string, fn: Function, onceFlag: boolean = false) { const fnList = this.eventRegister.get(event); if (fnList) fnList.push({ fn, onceFlag });// need to baesed on the structure elsethis.eventRegister.set(event, [{ fn, onceFlag }]);// need to baesed on the structure }
emit(event: string, info: string) {... }
off(event: string, fn: Function) {... }
}
This approach is intuitive and easy to understand, but it doesn’t feel elegant. As developers, we value elegance, so let’s use a neat trick to implement it. Here’s where the magic comes in:
off(event: string, fn: Function) { if (!this.eventRegister.get(event)) return; const fnList = this.eventRegister.get(event)?.filter(f => f !== fn); this.eventRegister.set(event, fnList!); }
once(event: string, fn: Function) { //nothing changed above, just to use a wrapper to implement this feature. constwrapper = (args) => { this.off(event, wrapper); fn(args); } this.on(event, wrapper);%t
// For newcomers to TypeScript, this might feel a bit hard to understand, but think of it this way: 1.this. on(event,fn()); // this is what we want, but we need to invoke the off() after fn() // then we create a wrapper to do it, and set the wrapper to this.on(..) } }