Yes, another article on Decorators… Here, we want to understand a little bit more on the runtime execution of decorators.
Decorators use the form
expressionmust evaluate to a function that will be called at runtime with information about the decorated declaration.
— TypeScript documentation for decorators
The above sentence is a little bit vague to beginners. We understand that decorator functions would execute at runtime. But when at runtime does decorator function get called? There are 3 possible time points: (1) when the original property/method/class gets called; (2) when an object instance gets created; (3) when the class prototype gets defined.
In order to answer the above question, I created a simple decorator and added some console logs to determine the runtime order. Complete code is in this TypeScript Playground.
The console output is as follows:
method "sayHi" decorator: begin
method "sayHi" decorator: end
method "sayBye" decorator: begin
method "sayBye" decorator: end
Class Constructor: called
Class Constructor: called
By looking at the console output, we already know the answer: Decorator functions execute during the class
Person prototype definition stage. That says, the prototype of
Person class has already added on decorators even before any new instance gets created or any owned method gets called.
If you comment out code lines 40–50, you will find that the decorator function still executes, even though no one instantiates a
Person object. This result tells us that, as soon as the script runs, the decorated
Person class is available in memory for future usages. And the prototypes of decorated methods have also updated to be the new descriptor value. For example, the prototype for
sayBye() method is pointing to line 13 in memory, see below screenshot.
You might have noticed that I put a counter
i in code line 11 and increment the counter
i in line 14. This counter can be used to track how many times a decorated method gets called. In code lines 42–47, two instances of
Person objects are created and
sayBye() method are called twice for each
Person object. From the console output, we can infer that the counter continues increasing despite of that the method callers are two different objects. This is because that the counter is stored in memory within the decorator function, which is inside the
Person class prototype, instead of individual object instance.
So, what can we take away from above tests?
- Decorators are applied to class prototype, not to object instances.
- Class prototype is decorated (decorators are executed) and stored in memory before class constructor or owned methods get called.
Feel free to play with this TypeScript Playground. And I appreciated it very much if you leave comments below.
🍺🍺 Cheers 🍻 🍻