Exploring JavaScript prototypes via TypeScript’s class pattern

Note: All the diagrams below were created using Gliffy, a fantastic web-based diagram editor. The latest version has been rebuilt using HTML and JS instead of Flash, and it really is a joy to use.

In this post, I’m going to take a close look at how JavaScript’s prototypal inheritance works by analysing how Microsoft’s TypeScript language uses it to provide a simple implementation of classes. Don’t worry if you’ve never used TypeScript, we’re not really concerned with the language itself here, just the JavaScript that is produced by its compiler, and how it achieves class-based inheritance in a language that doesn’t natively support it.

This is of particular importance because EcmaScript 6 will include classes that work a lot like TypeScript’s. The idea of adding classes to JavaScript is controversial, but I think some of the hostility stems from a mistaken belief that classes are some kind of competing inheritance mechanism to prototyping, or are an attempt to turn JavaScript into Java. In fact, modern proposals for classes in JavaScript are just a syntactic codification of prototypal inheritance patterns that are already in use.

I’m going to copy the approach Joost Diepenmaat used in his excellent article Constructors Considered Mildly Confusing,  by providing diagrams to illustrate the actual objects, properties and relationships that result from particular code statements. Incidentally, if you haven’t read Joost’s article, I recommend you do so. If you’re like me, and have frequently muddled through JS prototyping like it’s a JobInterviewQuicksort, then it’s a great introduction to how everything actually works under the hood.

The TypeScript Code

In case you’re unfamiliar with it, TypeScript is Microsoft’s entry into the crowded altJS language space, also occupied by the likes of CoffeeScript, Dart, Objective-J, and a wealth of others. TypeScript seems to stick closer to the EcmaScript 6 vision of JavaScript.next than most of the others, while also adding features like static typing and generics that will, depending on your prejudices, either enable sophisticated, grown-up programming on the web, or placate .NET developers too scared to use a dynamic language ;-). Perhaps it’s my many years spent coding with C#, but TypeScript feels a little more pleasant to me than Dart (although they share a lot of similarities).

Like many of these “transpiled” languages, the TypeScript site has an interactive playground where you can write some TypeScript code on one side of the screen and see the equivalent compiled JavaScript on the other. One of the provided examples demonstrates TypeScript’s class inheritance. If you’ve ever wrangled with Microsoft’s ASP.NET Ajax Library type system and its mechanisms for class and namespace registration and inheritance, you might be a little apprehensive here, but what TypeScript provides is much simpler, embedding a simple extension mechanism that builds on JavaScript’s standard prototypal inheritance.

Let’s briefly look at the original TypeScript code:

[javascript]
class Animal {
constructor(public name: string) { }
move(meters: number) {
alert(this.name + " moved " + meters + "m.");
}
}

class Snake extends Animal {
constructor(name: string) { super(name); }
move() {
alert("Slithering…");
super.move(5);
}
}

class Horse extends Animal {
constructor(name: string) { super(name); }
move() {
alert("Galloping…");
super.move(45);
}
}

var sam = new Snake("Sammy the Python");
var tom: Animal = new Horse("Tommy the Palomino");

sam.move();
tom.move(34);
[/javascript]

This should be easy to follow, even if you’ve never seen TypeScript before. It declares a base-class Animal with a constructor and a move instance method, and two classes, Snake and Horse that inherit from it. The inheriting classes have their own constructors, which call the superclass constructor. They also provide overriding implementations of the move method that also call the corresponding superclass method.

The Compiled JavaScript

Here is the the same code after being compiled into JavaScript by the TypeScript compiler:

[javascript]
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};

var Animal = (function () {
function Animal(name) {
this.name = name;
}
Animal.prototype.move = function (meters) {
alert(this.name + " moved " + meters + "m.");
};
return Animal;
})();

var Snake = (function (_super) {
__extends(Snake, _super);
function Snake(name) {
_super.call(this, name);
}
Snake.prototype.move = function () {
alert("Slithering…");
_super.prototype.move.call(this, 5);
};
return Snake;
})(Animal);

var Horse = (function (_super) {
__extends(Horse, _super);
function Horse(name) {
_super.call(this, name);
}
Horse.prototype.move = function () {
alert("Galloping…");
_super.prototype.move.call(this, 45);
};
return Horse;
})(Animal);

var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino");

sam.move();
tom.move(34);
[/javascript]

The code has roughly the same shape as the TypeScript code, but there is a bit more going on. The class definitions have been replaced with immediately-invoked anonymous functions, and a new function __extends has been inserted at the top. This function may only be four lines long, but it is the key to TypeScript’s implementation of class inheritance.

Basic Function Declarations

Let’s step through the code section by section, as if it were executing, and visualise exactly what’s happening with diagrams.

The first thing that happens is that the __extends function is declared and defined, if it has not already been defined elsewhere by some other compiled TypeScript code. We can ignore the actual contents of this function for now, and use a diagram to explore what a function declaration such as this one actually corresponds to behind the scenes.

[javascript]
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
[/javascript]

extends

Note: These diagrams can be interpreted as follows: Every ellipse represents an object. Yellow ellipses are regular objects, whereas the blue ellipses with names ending in parentheses are function objects. Arrow connections represent properties of an object, where the connection’s label is the property name, the arrow points from the object to which the property belongs, to the object referenced by the property. For example, in the diagram below, the __extends function object has a property named prototype that references the __extends.prototype object.

Properties with a solid purple connection are regular JavaScript properties. The kind you can access using the dot or square-bracket syntax in  JS code. Properties with a dotted green connection are internal properties, belonging to the runtime. They cannot be accessed directly by code ([[Prototype]] can accessed indirectly through other methods in some JavaScript runtimes, but that’s beyond the scope of this discussion).

Here we see that __extends is a function object, whose internal [[Prototype]] property points to Function.prototype, which in turn has a [[Prototype]] property pointing to Object.prototype. The __extends function object also has a regular property called prototype that references an object we’ll call __extends.prototype. This object has a property called constructor that references the __extends function object. Neither the prototype or constructor properties are internal like [[Prototype]]. They are regular JavaScript properties and can be accessed in code and overwritten, as we shall see.

The code didn’t explicitly create the __extends.prototype object or set these properties. Rather, the JavaScript runtime set it all up automatically, as it does for every function declaration. This is why every JavaScript function can be used as a constructor with the new operator. Although it’s not intended to be a constructor, it is possible to call new on __extends. The result would be a new object whose [[Prototype]] property pointed to __extends.prototype.

This can all be a bit of a headache at first, but grasping the difference between the constructor function, its prototype property, the referenced prototype object, and the internal [[Prototype]] property is vital to really understanding inheritance in JavaScript. It is the next step beyond just reusing the standard prototype and closure patterns to create objects.

Class Definitions

Moving on, the next part of the code to execute will be the declaration of Animal

[javascript firstline=”8″]
var Animal = (function () {
function Animal(name) {
this.name = name;
}
Animal.prototype.move = function (meters) {
alert(this.name + " moved " + meters + "m.");
};
return Animal;
})();
[/javascript]

Because Animal is a base-class within TypeScript, it doesn’t make use of the __extends function. Instead, we can see that it has been compiled into a pretty standard prototype pattern declaration, which is returned as the result of an immediately-invoked anonymous function and assigned to variable. The only possibly confusing thing is the duplication of the name Animal for both the function inside the anonymous function and the variable in the parent scope. Every use of Animal within the anonymous function references the function declaration, whereas every use outside references the variable that gets assigned to the result of the anonymous function. However, since the former is what gets assigned to the latter, it makes no practical difference.

Of course, although it doesn’t use TypeScript’s inheritance, Animal and the objects created by new-ing it will still participate in standard JavaScript inheritance. By default, objects in JavaScript have a [[Prototype]] chain leading to Object.prototype. The end result will be very similar to the __extends declaration I depicted above, except that in this case the Animal function is intended for use as a constructor with the new operator.

Animal

The only difference between this layout and __extends‘s is that the Animal.prototype object has a move property, and that property is assigned to a function object (which, of course, is also an object, and has an internal [[Prototype]] property referencing Function.prototype.)

Class Instances

The Animal class isn’t used directly, but what if it was? Imagine the next line of code was as follows…

[javascript light=”true”]
var jim = new Animal("Jim the Possum");
[/javascript]

Here we’ve new-ed an instance of Animal assigned to a variable called jim. What would this give us?

jim

To avoid overcomplicating the diagram, I’ve removed some of the unimportant [[Prototype]] links and the Function.prototype object, but in reality they would still be there. Instead, let’s concentrate on what’s happening with the jim variable: It is an object, whose internal [[Prototype]] property points to Animal.prototype, and therefore it will inherit the properties of that object, namely the move property and its associated function. Calling jim.move() will execute Animal.prototype.move, unless we have overridden it by setting a move property on the jim object itself.

Class Inheritance

OK, so this is all pretty standard stuff, but now things get more interesting, as execution reaches the declaration of the Snake class, which is as follows:

[javascript firstline=”18″]
var Snake = (function (_super) {
__extends(Snake, _super);
function Snake(name) {
_super.call(this, name);
}
Snake.prototype.move = function () {
alert("Slithering…");
_super.prototype.move.call(this, 5);
};
return Snake;
})(Animal);
[/javascript]

Again we have an immediately-invoked anonymous function, but this time it takes a single argument, Animal. This argument is referred to as _super within the anonymous function body. Leaving aside the __extends call for a second, we can see that the body of the anonymous function contains another prototypal declaration, similar to that of the Animal declaration above. If we compare the code with the original TypeScript, we can see that the calls to the superclass, which used the super keyword in TypeScript, have been compiled to use the _super argument.

For Snake, _super references the superclass constructor Animal, and _super.prototype property references the Animal.prototype object. The Snake class can access the behaviour of its superclass by call-ing its constructor and its prototype methods and passing in the this reference, which will execute the functions in the context of the subclass instance.

Now, what about that __extends call? Although it appears first in the anonymous function body, the Snake function declaration will be hoisted above it by the runtime. Therefore __extends will execute after Snake is declared, but before anything is assigned to Snake.prototype, giving it an opportunity to modify that property first. It is called with two arguments, the Snake function and the _super object.

As we saw earlier, the code for the __extends function is quite terse, so here’s a slightly expanded version that might be easier to parse:

[javascript]
var __extends = this.__extends || function (subclass, superclass) {
for (var propertyName in superclass) {
if (superclass.hasOwnProperty(propertyName)) {
subclass[propertyName] = superclass[propertyName];
}
}

function subclassPrototype() { this.constructor = subclass; }
subclassPrototype.prototype = superclass.prototype;
var newPrototype = new subclassPrototype();
subclass.prototype = newPrototype;
};
[/javascript]

The first thing the function does is to loop through every enumerable property of the superclass and copy it to the subclass. Note that it is the properties of the superclass constructor function that are copied, not those of its prototype. TypeScript likely does this to allow subclasses to access “static” members of their superclass.

The next four statements are the trickiest part. As we saw earlier, the runtime will have created a default prototype object for the subclass function, with its [[Prototype]] property referencing object.prototype. In order to allow instances of the subclass to inherit the superclass properties, the extension mechanism needs to create a replacement prototype object whose [[Prototype]] property points to the superclass prototype. Lets go through the statements one by one.

[javascript firstline=”8″]
function subclassPrototype() { this.constructor = subclass; }
[/javascript]

First, there is a function declared, which I have renamed subclassPrototype. As before, a regular function declaration like this will result in a function object and an automatically created prototype object with a [[Prototype]] chain pointing to object.prototype.

subclassPrototype

Next the prototype property of the subclassPrototype function is assigned to the superclass’s prototype object.

[javascript firstline=”9″]
subclassPrototype.prototype = superclass.prototype;
[/javascript]

subclassPrototype2

The constructor property of the automatically generated prototype object is still pointing to subclassPrototype, but that won’t have any effect, and we can forget about subclassPrototype.prototype from now on.

[javascript firstline=”10″]
var newPrototype = new subclassPrototype();
[/javascript]

Next we declare a new variable newPrototype and assign it to a new-ed up instance of subclassPrototype. It’s important to understand what happens here. Calling new on a function triggers the following process within the runtime:

  1. A new object is created.
  2. The new object’s internal [[Prototype]] property is assigned to the value of the constructor function’s regular prototype property.
  3. The constructor function is executed in the context of the new object. E.g. within the function, the this keyword will be bound to the new object.
  4. The new object is returned as the result of the whole new subclassPrototype() expression.

This process will result in an object newPrototype whose [[Prototype]] property points to the superclass prototype. This means it will inherit the constructor property from the superclass prototype, but because this prototype is intended for the subclass, that is not correct. The constructor property for newPrototype should reference the subclass function, therefore subclassPrototype reassigns it within the constructor function. The end result is an arrangement as follows:

newPrototype

We now have a newPrototype object with the correct [[Prototype]] chain, and the correct value for constructor.

[javascript firstline=”11″]
subclass.prototype = newPrototype;
[/javascript]

The final step is just to reassign the prototype property of subclass to the newPrototype object. This takes the default subclass.prototype object out of the picture leaving us with the following arrangement:

newPrototype2

And this is exactly what we want. From now on, new-ing an object with the subclass function will result in an object whose internal [[Prototype]] property points to newPrototype, which in turn allows it to inherit from the superclass prototype and also gives its a constructor property that points back to the subclass function.

The Class Hierarchy

At this point, the __extends function finishes executing, and control passes back to Snake‘s anonymous function. The code can then proceed to add properties to Snake.prototype, which will now reference the correctly configured prototype object that __extends created.

The Snake class declaration is followed by the Horse class declaration, which works identically. When all three classes have been declared, the result will be a prototype hierarchy where Horse.prototype and Snake.prototype both inherit from Animal.prototype, which in turn inherits from Object.prototype, as illustrated.

snakes-horses-oh-my

Using Classes

From then on, things are straightforward. Instances of Horse and Snake are new-ed up and assigned to variables in the usual way, then their instance methods are called.

[javascript firstline=”42″]
var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino");

sam.move();
tom.move(34);
[/javascript]

Based on everything we’ve gone through so far, hopefully the prototype hierarchy for the object instances should be fairly obvious:

sam-and-tom

Accessing properties of the sam and tom objects acts we would expect. Since neither object has its own property called move, the JavaScript runtime looks up the [[Prototype]] chain for a match. The move functions referenced by Snake.prototype and Horse.prototype will be found first, and therefore hide the move function defined on Animal.prototype. However, the use of the _super object within the definitions of those functions means that the Animal implementation of move is still run.

Wrapping Up

Hopefully all that was reasonably intelligible and will be of use to some people. Doing worked examples like this is an excellent way to get a stronger grasp on an area of JavaScript that can be fuzzily understood if you spend most of your time writing code that consumes frameworks and doesn’t mess around with prototypes too much.

As with any technical article like this, there are likely errors and things I’ve misunderstood. Please let me know of any problems via the comments.


Posted

in

,

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *