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.onRelayed
is passed toMessageRelayer
. It means that in that moment,this
forTestClass.onRelayed
will be bound toMessageRelayer
. - As soon as
relayMessage
is invoked,MessageRelayer
will get the saved instance ofTestClass.onRelayed
whosethis
is now pointing toMessageRelayer
, and executed it. - I should get an error that
this
does not containel
.
Instead I find out that this
is actually undefined
. What is happening here?