Decorator’s runtime order?

July 08, 2018 0 Comments

Decorator’s runtime order?

 

 

Yes, another article on Decorators… Here, we want to understand a little bit more on the runtime execution of decorators.

Decorators use the form @expression, where expression must 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
Main: Start
Class Constructor: called
sayBye0: begin
Bye
sayBye
0: end
sayBye1: begin
Bye
sayBye
1: end
Class Constructor: called
sayBye2: begin
Bye
sayBye
2: end
sayBye3: begin
Bye
sayBye
3: end

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.

try out this TS playground

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?

  1. Decorators are applied to class prototype, not to object instances.
  2. 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 🍻 🍻


Tag cloud