Classes Emit
What's up with the IIFE
The js generated for the class could have been:
The reason it's wrapped in an Immediately-Invoked Function Expression (IIFE) i.e.
has to do with inheritance. It allows TypeScript to capture the base class as a variable _super
e.g.
Notice that the IIFE allows TypeScript to easily capture the base class Point
in a _super
variable and that is used consistently in the class body.
__extends
__extends
You will notice that as soon as you inherit a class TypeScript also generates the following function:
Here d
refers to the derived class and b
refers to the base class. This function does two things: 1. copies the static members of the base class onto the child class i.e. for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
1. sets up the child class function's prototype to optionally lookup members on the parent's proto
i.e. effectively d.prototype.__proto__ = b.prototype
People rarely have trouble understanding 1, but many people struggle with 2. So an explanation is in order.
d.prototype.__proto__ = b.prototype
d.prototype.__proto__ = b.prototype
After having tutored many people about this I find the following explanation to be simplest. First we will explain how the code from __extends
is equivalent to the simple d.prototype.__proto__ = b.prototype
, and then why this line in itself is significant. To understand all this you need to know these things:
__proto__
prototype
effect of
new
onthis
inside the called functioneffect of
new
onprototype
and__proto__
All objects in JavaScript contain a __proto__
member. This member is often not accessible in older browsers (sometimes documentation refers to this magical property as [[prototype]]
). It has one objective: If a property is not found on an object during lookup (e.g. obj.property
) then it is looked up at obj.__proto__.property
. If it is still not found then obj.__proto__.__proto__.property
till either: it is found or the latest .__proto__
itself is null. This explains why JavaScript is said to support prototypal inheritance out of the box. This is shown in the following example, which you can run in the chrome console or Node.js:
Cool so you understand __proto__
. Another useful fact is that all function
s in JavaScript have a property called prototype
and that it has a member constructor
pointing back to the function. This is shown below:
Now let's look at effect of new
on this
inside the called function. Basically this
inside the called function is going to point to the newly created object that will be returned from the function. It's simple to see if you mutate a property on this
inside the function:
Now the only other thing you need to know is that calling new
on a function assigns the prototype
of the function to the __proto__
of the newly created object that is returned from the function call. Here is the code you can run to completely understand it:
That's it. Now look at the following straight out of __extends
. I've taken the liberty to number these lines:
Reading this function in reverse the d.prototype = new __()
on line 3 effectively means d.prototype = {__proto__ : __.prototype}
(because of the effect of new
on prototype
and __proto__
), combining it with the previous line (i.e. line 2 __.prototype = b.prototype;
) you get d.prototype = {__proto__ : b.prototype}
.
But wait, we wanted d.prototype.__proto__
i.e. just the proto changed and maintain the old d.prototype.constructor
. This is where the significance of the first line (i.e. function __() { this.constructor = d; }
) comes in. Here we will effectively have d.prototype = {__proto__ : __.prototype, constructor : d}
(because of the effect of new
on this
inside the called function). So, since we restore d.prototype.constructor
, the only thing we have truly mutated is the __proto__
hence d.prototype.__proto__ = b.prototype
.
d.prototype.__proto__ = b.prototype
significance
d.prototype.__proto__ = b.prototype
significanceThe significance is that it allows you to add member functions to a child class and inherit others from the base class. This is demonstrated by the following simple example:
Basically bird.fly
will be looked up from bird.__proto__.fly
(remember that new
makes the bird.__proto__
point to Bird.prototype
) and bird.walk
(an inherited member) will be looked up from bird.__proto__.__proto__.walk
(as bird.__proto__ == Bird.prototype
and bird.__proto__.__proto__
== Animal.prototype
).
Last updated