I am creating a library and one of the patterns I need to implement is event handling. I am writing in TypeScript, but this issue is basically in Javascript. Consider this code:
class MessageRelayer {
private readonly listeners: Array<(args: string) => void> = [];
public addListener(listener: (args: string) => void): void {
this.listeners.push(listener);
}
public relayMessage(msg: string): void {
for (const listener of this.listeners) listener(msg);
}
}
class TestClass {
private el: string;
constructor(private readonly relay: MessageRelayer) {
this.el = "example";
}
public initialize(): void {
this.relay.addListener(this.onRelayed);
}
private onRelayed(e: string): void {
console.log("EL is:", this.el);
}
}
const relay = new MessageRelayer();
const stuff = new TestClass(relay);
stuff.initialize();
relay.relayMessage("Hello world"); // Error here
When running this code, it gives me error in onRelayed:
Cannot read property 'el' of undefined
If initialize is written as:
public initialize(): void {
this.relay.addListener((e: string) => { console.log("EL is:", this.el); });
}
It works.
Questions
I understand this is about the famous this binding issue. But what's happening is not something I can explain here. What I think it should happen is:
- When calling
initialize(), functionTestClass.onRelayedis passed toMessageRelayer. It means that in that moment,thisforTestClass.onRelayedwill be bound toMessageRelayer. - As soon as
relayMessageis invoked,MessageRelayerwill get the saved instance ofTestClass.onRelayedwhosethisis now pointing toMessageRelayer, and executed it. - I should get an error that
thisdoes not containel.
Instead I find out that this is actually undefined. What is happening here?