Class fields can cause harm oh naur
Article by Andrea Giammarchi
Motivation
In my pursuit to better understand JavaScript underthehood™ I've seen it been mentioned that there are
obscure differences between methods and arrow functions on Classes e.g. handleClick() {}
vs.
handleClick: () => {}
. I'd like to learn more about the potential pitfalls.
What are the tradeoffs when I use arrow functions? When should I bind this
? When should I
call(this)
What are some alternatives?
Let's get some basics out of the way
Wait..what's a class field again? *GPT-ing* Ok, it's basically a property that can
either be private or public; private fields are prefixed by a #
. When naming fields there are two
that are off-limits: prototype and constructor. Duh.
Point #1
If you create a method using an arrow function, there is no way to override the method if you plan to
extend the class.
this
only exists once super() is called which needs to be called
immediately
after the constructor is declared. It means any method overrides with the same name will only be scoped
locally. NOT the this
of the parent class.
When extending a Class, the required super() already has a context
this
. Creating a method
using an arrow function in the extended child instance cannot alter the previously defined method on the
parent.
Ok, I give up trying to understand/explain this in my own words. Here's the GPT explanation:
Why This Happens: To understand why this happens, we need to look at how class fields are de-sugared (translated) into traditional JavaScript code that uses prototypes behind the scenes. When you define a class field in a class, such as Counter, the actual method becomes a property of the instance created from that class. In the case of DoubledCounter extending Counter, the method from the parent class is already attached to the instance before the super() call in the subclass's constructor is executed. This means that when you try to override the method in the subclass, you're actually creating a new method that gets assigned after the initial parent method is already attached.
I was still a bit unclear as to why redefining a method doesn't override the parent's method regardless of definition order. Does that mean subclass methods with matching parent class methods can never be overridden?
Point #2
There are a few ways to get the expected behavior:
- Stick to the old way handler.bind(this)
-
Cut out all the sugar and define your method on the
prototype
- Via accessor
get
- Double arrow functions?? This is getting too complicated
- Last but not least: handleEvent()* This will be the focus of my next post as I'll hopefully be using it extensively in future projects
Cool bits
I learned what a class field is and better yet I learned it can be
computed.[`${taco}`] = 'suadero'
I don't think I answered my original questions that motivated me to read this article in the first place. Though, I'm glad I decided to start writing again.