I am playing with Typescript and trying to understand the compiled Javascript code generated by the compiler
Typescript code:
class A { }
class B extends A { }
Generated Javascript code:
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var A = /** @class */ (function () {
function A() {
}
return A;
}());
var B = /** @class */ (function (_super) {
__extends(B, _super);
function B() {
return _super !== null && _super.apply(this, arguments) || this;
}
return B;
}(A));
The Javascript inheritance as per Mozilla docs is this:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
The parts that I do not understand in the Typescript's generated code are this
1. What is the purpose of this line? Looks like it is copying all the keys of A into B? Is this some sort of a hack for static properties?
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
2. What is this doing?
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
I don't understand this part: (__.prototype = b.prototype, new __())
Why does function B() return this?
return _super !== null && _super.apply(this, arguments) || this;
If someone can explain this to me line by line I would be grateful.
I was curious about this myself and couldn't find a quick answer so here is my breakdown:
What it does
__extends is a function that simulates single class inheritance in object oriented languages and returns a new constructor function for a derived function that can create objects that inherit from a base object.
Note 1:
I wasn't actually aware of this myself but if you do something like the following where all of the values are truthy the variable is set to the value of last item being tested unless one is falsy in which case the variable is set to false:
I mention this because this is the thing that confused me the most when I saw this javascript and it is used a couple of times in the __extends function defintion.
Note 2: Parameter d (probably stands for derived) and b (probably stands for base) are both constructor functions and not instance objects.
Note 3:
prototype
is a property of a function and it is a prototype object used by 'constructor' functions (i.e. objects created by usingnew <function name>()
).When you use the
new
operator to construct a new object, the new object's internal[[PROTOTYPE]]
aka__proto__
is set to be the function's prototype property.It isn't a copy. It IS the object.
When you create a literal object like
the new object's
__proto__
is set toObject.prototype
(the built-in Object constructor function).You can set an object's
__prototype__
to another object however usingObject.create
.When a property or method isn't found on the current object the object's
[[PROTOTYPE]]
is checked. If it isn't found then THAT object's prototype is checked. And so it goes checking prototypes until it reaches the final prototype object,Object.prototype
. Keep in mind nothing is a copy.Note 4 When simulating inheritance in Javascript the 'constructor' functions' prototypes are set.
Notice how we point the constructor to Girl because it Person's constructor points to the built-in
Function
.You can see the code above in action at: http://jsbin.com/dutojo/1/edit?js,console
Original:
The break down:
Keeping my Note 1 above in mind, this first part (and end) is creating a variable called __extends that has the intention of holding a function to set the prototype of the derived class.
is doing what my Note 1 explains. If this is truthy and this.__extends is truthy then the variable __extends is already exists and so set to the existing instance of itself. If not it is set to what comes after the || which is an iife (immediately invoked function expression).
Now for the gobbledygook which is the actual definition of __extends:
A variable named extendStatics is set to either the built in Object.setPrototypeOf function of the environment that the script is running in (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf)
OR
it creates its own version
In Note 3 I discussed
__proto__
aka[[PROTOTYPE]]
and how it could be set. The codeis a test to determine whether the current environment allows setting this property by comparing a literal object's
__proto__
set to a literal array with the Array built-in function.Referring back to my Note 1 from above and keeping in mind that the javascript instanceof operator returns true or false (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) if the environment evaluates an an object with its prototype property set to the built in Array then extendsStatics is set to
If the environment doesn't evaluate it that way then extendStatics is set to this:
It does this because
__proto__
was never part of the official ECMAScript standard until ECMAScript 2015 (and according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) is there only for backwards compatibility). If it is supported the__proto__
function is used or else it uses the 'roll your own' version' which does a copy from object b to d for user defined properties.Now that the extendStatics function variable defined, a function that calls whatever is inside extendStatics (as well as some other stuff) is returned. Note that parameter 'd' is the sub-class (the one inheriting) and 'b' is the super-class (the one being inherited from):
Breaking it down extendStatics is called and the first parameter object (d) has its prototype set to be (b) (recall Note 3 above):
In the next line a constructor function named '__' is declared that assigns its constructor to be the derived (d) constructor function:
if the base (b)
constructor
function happens to be null this will make sure that the derived will keep its ownprototype
.From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor, the Object.prototype.constructor (all objects are Objects in javascript):
And
So if
constructor
function '__' was new'd up as is it would create a derived object.Lastly there is this line:
which sets the
prototype
of the derived (d) to be a new empty object if the baseconstructor
function happens to be nullOR
sets the __
constructor
function'sprototype
to be the base classprototype
and then calls __() which has the effect of setting the derived functionsconstructor
to be the derived function.So basically the final function returned creates a derived constructor function that prototypically inherits from the base constructor function.
Why does function B() return this?
According to: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
Remember B is a constructor function and that is what is being returned in B's definition.
If you had a Person class (constructor function) that accepted a name parameter in the constructor function you could then call a derived Girl class (constructor function) with the girls name as a parameter.