JavaScript's 42: proto, prototype & Co. (Part III)
This is the third post on inheritance in JavaScript.
Where in the first, and second post was about proto
, prototype
and Object.create
(in JavaScript), this post focusses on the inheritance in TypeScript.
More specifically, this post presents the details about what is going on behind the scene with inheritance in TypeScript.
Before you start: If you are not sure about functionalities of proto
, prototype
and Object.create
, then you must (re)visit these concepts. You may refer my earlier blog post (as said above) to brush up these concepts.
As you may already know that TypeScript codes are finally gets compiled into plain JavaScript code. Thus, this post focusses on how the transformed javascript looks like.
If you have gone through my earlier blog posts in this series then you know that to use inheritance in plain JavaScript, we need to do quite a lot of stuffs.
With TypeScript the things get easier, as here you just have to use the extends
keyword, where the magic happens. Example:
And that’s all you need to do in order to use inheritance in TypeScript. Magic right?
Well, not quite.
If we check the JavaScript code generated by the TypeScript compiler, we will see the same rigorous implementation for inheritance in JavaScript.
When I compile this TypeScript code, the following JavaScript is generated. Depending on your version of TypeScript (mine is 2.5.3
at the time of writing this post) you may have a slightly different version.
Though it may look too much crammed up alien code, they are plain JavaScript. If you have gone through the blog posts in this series then you know that we can breakdown the task of implementing inheritance into two parts:
- Inheriting the instance-scoped properties (defined on
this
) and members (defined onprototype
) of base class, and - Inheriting the
static
s.
For the first part, we look at the __extends
function, which is roughly defined as follows:
Here d
, and b
stands for derived and base class respectively.
And then we have the regular drill.
In the first line of the function it copies the static
members of base class by invoking extendStatics
.
Then in the last two lines it assigns the prototype
of base class to d.prototype.__proto__
, which is needed to inherit the members defined on prototype
of base class (I am sure you remember the basics).
As we know that we are (sort of) replacing the prototype
of the derived class, any member defined on prototype
of derived class before doing the gymnastic of inheritance is lost (refer the second post).
That’s why the first thing done in the derived class (in our case Bird
) is to call __extends(Bird, _super);
.
Note that _super
is supplied to the derived class by wrapping the code of derived class in a Immediately-Invoked Function Expression (IIFE), and passing Animal
as value for _super
. Simplified code chunk below:
Not so magical now is it?
Next thing we need to check is what happened behind the call of extendStatics(d, b)
, which inherits the static members of base class? Well, that part is also straightforward if we break up the definition of extendStatics
, as it is done below.
We see that here we have actually 3 options for the implementation. It checks if an alternative is available in the JavaScript engine; if it is then use it else fallback to next alternative. The last one is our good old inheritStatics
. So nothing is new about that.
The first and second alternatives are the new stuffs we have here. However, both of them does the same thing; i.e. to assign the base class to the __proto__
of the derived class (you may consider to check the polyfill of Object.setPrototypeOf
on MDN at this point). The second alternative additionally checks before succeeding if the property __proto__
is supported by JavaScript engine or not (as it was used to be a non-standard property). And it does that by assigning an empty array to __proto__
of an object and then checking if the object is an Array
or not. But the main juice of these two alternatives is this part: d.__proto__ = b
. Below is an example to show this.
And once you set Animal
to Bird
’s __proto__
, whenever you want to access any static member from Bird
JavaScript will climb the inheritance/prototypical ladder for you and fetch the member; i.e. Animal
’s statics are now accessible from Bird
.
And that’s how extends
in TypeScript translates to JavaScript.
That’s it then? Well, before ending this post I want to point out one major difference between the first 2 alternatives and the last one. If we look closely then we see that the first 2 alternatives handles the problem of inheriting statics by setting the __proto__
of derived, whereas the third alternative makes a copy of static members of base class. This makes a huge difference for simple value-properties, as making copies of those prevents pointing to the same value-property as copying makes those available directly to derived class, and thereby preventing the climb of prototypical ladder for those property if not explicitly done so. To demonstrate the difference we recall the population example shown in the first post. A brief version of the example is shown below:
Now if we define the extendStatics
as the third fallback option then we get following results.
However, if we use Object.setPrototypeOf
to inherit the statics we will get a different result, as shown below.
The difference of the results are explained by the difference in the behavior of how these 2 alternatives works; i.e. Object.setPrototypeOf
sets the __proto__
of derived class to base class, whereas the other one merely makes copy and thus points to different instances.
Thus, to replicate the former behavior while using Object.setPrototypeOf
we can simply define the population
member on subclasses. Example:
If you are wondering if the same happens with TypeScript as well, then I invite you check it by yourself. However, you already know the answer, isn’t it? And with that I will close this post.
Hope this helps.