JavaScript's 42: proto, prototype & Co. (Part I)
After years of working with .NET, SQL, and C#, for last couple of years I am working with TypeScript, Aurelia, Node.js, and Webpack (of course after working with SystemJs). Finally, now I have got the chance and the time to formally learn JavaScript.
As I am thoroughly enjoying the learning, I decided to jot down some rough notes about that.
So, there will be a series of posts about inheritance in JavaScript, and certainly we will be talking about proto
, prototype
& Co. in these posts.
Therefor, the posts are more for me, rather than for you. If you like it then it is a bonus for me. If you have any feedback, please don’t hesitate to leave a comment.
What is __proto__
?
Every JavaScript object has a property called __proto__
. Example:
The example above shows that __proto__
of obj
is defined but empty.
When we try to access the member of an object (like object.member
), JavaScript looks up for the member in __proto__
of the object if the member is not found in the object itself (like object.__proto__.member
). If the member is still not found in object.__proto__
, then it tries for object.__proto__.__proto__.member
till either: it is found or the latest
.__proto__
itself isnull
. This explains why JavaScript is said to support prototypal inheritance out of the box. - From TypeScript Deep Dive.
Example:
What is prototype
?
Every function
f
in JavaScript also has a property called prototype
, which has a property constructor
which points to f
itself. Example:
__proto__
and inheritance
Basics
Because JavaScript tries to fetch member from __proto__
recursively, __proto__
is useful to implement prototypical inheritance.
A simple way to implement inheritance would be to assign <Base>.prototype
to __proto__.__proto__
to an object, which wants capabilities of <Base>
. Example:
We note here that fly
is defined on Bird.prototype
, and we access that from bird
object. This is possible as fly
is made available to bird.__proto__
, by new Bird()
.
In fact, var bird = new Bird()
assigns Bird.prototype
to bird.__proto__
. In JavaScript, this makes all members defined on class’ prototype level (i.e. Type.prototype
, here Bird.prototype
) to the instances of that class (Refer this).
The fact that bird.__proto__ === Bird.prototype
enables us to write the inheritance for the Bird
class from Animal
class more concisely, as follows.
Note: From MDN:
The
instanceof
operator tests whether an object in its prototype chain has the prototype property of a constructor.
Accessing instance-scoped properties of base class
So far our inheritance implementation works quite good. Let’s consider other scenarios. With inheritance, we can access the instance-scoped properties of the class. Let’s see if we can do that using our implementation of inheritance. For this, we add some new properties to the classes (such as claws
in Animal
, and wings
in Bird
).
We see that using our current implementation, we can’t access the instance-scoped property claws
which is defined in Animal
.
However, before delving into that, let’s quickly discuss what this
means with new Bird()
. From TypeScript Deep Dive:
… let’s look at effect of
new
onthis
inside the called function. Basicallythis
inside the called function is going to point to the newly created object that will be returned from the function. …
Example:
From the example we see that this
in the function eventually points to bird
object (instance of Bird
), and thereby making all the properties declared on this
inside the function, available to the bird
object. Additionally we also note that wings
exists directly on bird
object and not on bird.__proto__
.
Therefore, to reuse the properties defined in base classes from the instance of the derived classes, we need to put the properties of base class to the this
object of derived class. The common way to achieve this is to call the constructor of base class from the constructor of derived class (like we do in C#, or Java).
So the trick to call the constructor of Animal
class is to use Animal.call(this);
in constructor of Bird
. Here we need a few words about function.call
. From MDN:
The
call()
method calls a function with a giventhis
value and arguments provided individually.
Syntax:
function.call(thisArg, arg1, arg2, ...)
Where thisArg
is
The value of this provided for the call to a function. …
and arg1, arg2, ...
are
Arguments for the function.
A quick example of function.call
is following.
We see that based on different values of thisArg
parameter we can produce different results from the same function.
And this is what is done to inherit the instance-scoped properties from the Base class. So we call Base.call(this)
from the constructor of Derived
, where this
points to the instance of Derived
being constructed, and thereby putting the properties of Base
to the instance of Derived
.
Accessing static members of base class
We are almost done. One last thing is to inherit the static
members. Yes, using JavaScript that is possible. However, the first question is to how to have static
members in classes in JavaScript.
Well, static
members roughly refers to the members those are tied with class and not to the instances of the class, and thus when the static
members are updated, the updated value is available to all the instances of the class. So, we access the static
members are accessed like ClassName.staticMember
, or ClassName.staticMethod()
, and that is what we need to implement the static
members in class. Example:
Therefore, to create a static
property on class, we can simply define a property on the Class
, like it is done for StaticDemo.staticProp
. Recalling that var instance = new Class()
assigns the Class.prototype
to instance.__proto__
, a class member defined not on prototype
and directly on Class
instead, does not get tied with instances created and rather stays attached with the Class
. And that’s how the static properties are created in JavaScript.
Then how to inherit the static
members? Well, that is just a simple matter of copying the member (value-copy for simple-typed member, and reference-copy for object-typed member) from one place to another. So to do that we need another piece of function, as shown below.
From for...in
documentation in MDN:
A
for...in
loop only iterates over enumerable properties.
and from hasOwnProperty()
documentation in MDN:
The
hasOwnProperty()
method returns a boolean indicating whether the object has the specified property as own (not inherited) property.
The function inheritStatics
copies the properties defined on base
to derived
. In general, base
, and derived
can be any two general objects. Example:
Lastly, we see it in action for inheriting static
s from base class to derived class.
So, yes, on ground level that’s all we need to implement inheritance in JavaScript. This is quite something right?
Well, this has been a long post, as per my standard. However, there is still somethings left to discuss on this topic, which I am going to discuss on future posts (second post).
Hope this helps.