Event Emitter II
Prompt
In the first part of the Event Emitter challenge, you built a class that registers listeners and emits events. Now, let's improve its developer experience!
Currently, to attach multiple events or mix operations, you have to write code like this:
emitter.on('login', userHandler);
emitter.on('logout', logoutHandler);
emitter.emit('login', { user: 'Alice' });
emitter.off('login', userHandler);Your task is to modify the EventEmitter class so that the on, off, and emit methods can be chained together fluidly, like this:
emitter
.on('login', userHandler)
.on('logout', logoutHandler)
.emit('login', { user: 'Alice' })
.off('login', userHandler);The core logic of the three methods should stay exactly the same. Your goal is simply to allow calling them continuously on the same instance without breaking the chain.
Playground
In JavaScript, x.on().emit() means that the result of
x.on() must have an .emit() method available on it.
What should .on() return to make this possible?
Think about what object you want to continue calling
methods on. It's the EventEmitter instance itself! What
special keyword gives you a reference to the current
object instance inside a class method?
Solution
Explanation
What is Method Chaining?
Method chaining is a common pattern in JavaScript (seen frequently in libraries like jQuery or arrays like [].filter().map()). It allows you to write multiple method calls one after another in a single fluid statement.
How does it work?
When you write emitter.on('event', fn).emit('event'), JavaScript evaluates it step-by-step from left to right:
- It calls
emitter.on('event', fn). - Whatever that method returns is what
.emit('event')is called on.
If our on method doesn't explicitly return anything, JavaScript returns undefined by default. That's why attempting to chain without returning anything results in an error like Cannot read properties of undefined (reading 'emit'). You're literally trying to do undefined.emit().
The Magic Keyword: this
To fix this, we just need the method to return the emitter object itself so that the next method in the chain has the original object to work with. Inside a class method, the keyword this points directly to the current instance of the class.
By adding return this; to the very end of our on, off, and emit methods, we hand the instance right back to the caller, ready for the next method in the chain!