Prototypes in Javascript
Wednesday, December 30th, 2009Inheritance allows programmers to reuse code. Code reuse is the holy grail of software development best practices. The most common form of inheritance relies on an idiom known as the Class. The Class is a blueprint of what an object should look like once instantiated. It is declaritive and exists at compile time. Any Class can inherit another Class and override methods declared by it’s parent or add new methods.
Example (I hate using metaphors to describe software, but it will have to do):
A house blueprint defines a building with four walls and two windows. A villa blueprint adds plans for a jacuzzi and refers the builder to the house blueprint for everything else.
Another implementation of inheritance uses Prototypes instead of Classes. Prototypes are instantiated objects. Programmers take a Prototype and add or remove methods and properties to create a new object. Prototypal inheritance is procedural and happens at runtime.
Example (You’re about to see why I hate metaphors — they produce odd outcomes):
You build a prototype airplane. For this metaphor to work you need to imagine that this prototype can make infinite copies of itself, and the copies stay in perfect sync with eachother. Remember the Mirror Image spell? Like that. If the original prototype gets a scratch, all the copies instantly have the same scratch. If one of the copies is scratched, it doesn’t affect the original. Now, take one of those copies and replace the wings. That’s prototypal inheritance.
Using Classes, you are reusing the directions for building the object. Using Prototypes, you are reusing objects.
This is what prototypal inheritance looks like in a fake programming language I call OolongScript:
var biplane = { 'sound' : 'buzzZZZzzz',
'color' : 'blue',
'numWings' : 4,
'chanceOfFailure' : 60%;}
var jet = create(biplane); //jet is a magic copy of biplane
//Let's improve our biplane copy
jet.numWings = 2;
jet.chanceOfFailure = 5%;
jet.weaponry = "Smart Bomb";
//okay, how many wings does the jet have?
print jet.numWings
>> 2
//What color is the jet?
print jet.color
>> blue
//Did we mess up the biplane?
print biplane.numWings
>> 4
print biplane.weaponry
>> Undefined
//All airplanes should be red, not blue.
biplane.color = 'red'
//What color is the jet?
print jet.color
>> red
//I want an F22 Raptor, not some lousy generic jet
var raptor = create(jet)//Create a magic copy of the jet.
raptor.stealth = True
raptor.sound = "looooooooud"
print raptor.weaponry //should be "Smart Bomb" as defined on the jet.
>> Smart Bomb
print raptor.color //should be red, just like the biplane
>> red
The raptor is built on a magic copy of the jet which uses a magical copy of the biplane prototype as a base. The jet and raptor replace the bits they don’t want and add new stuff. This doesn’t affect the biplane. But when the biplane changes color, the jet is automatically updated, as is the raptor.
Prototypal Inheritance in JavaScript (not OolongScript)
Javascript facilitates inheritance using prototypes, but where OolongScript has “create” for making magic copies, JavaScript has constructor functions and the prototype property. This is not better, it’s just harder to understand. But working from the example above, we should be able to understand what is happening.
The code for creating the biplane is the same:
var biplane = { 'sound' : 'buzzZZZzzz',
'color' : 'blue',
'numWings' : 4,
'chanceOfFailure' : 60%;}
In JavaScript we want to ‘construct’ a jet by calling a function. It might look like this in OolongScript:
var this = create(biplane); //Set this to a magic copy of the biplane
var Jet = function(){
//Let's improve our biplane prototype
this.numWings = 2;
this.chanceOfFailure = 5%;
this.weaponry = "Smart Bomb";
return this;
}
var jet = Jet();
Except in JavaScript there is no ‘create’ function. So how does JavaScript set the variable ‘this’ to a magic copy of the biplane? Like so:
// The Jet function remains the same. 'this' is a JavaScript keyword that we don't need
// to define anywhere. We tell JavaScript what 'this' should be later using the 'new'
// keyword, as you'll see below.
var Jet = function(){
//Let's improve our biplane prototype
this.numWings = 2;
this.chanceOfFailure = 5%;
this.weaponry = "Smart Bomb";
return this;
}
//Set the prototype property of the function Jet
Jet.prototype = biplane;
// Calling a function with 'new' in front of it tells JavaScript to bind
// the keyword 'this' to a magic copy of the prototype property of
// the function Jet, which we set the line above.
var jet = new Jet();
This whole setup is really hard to wrap your head around. Understanding prototypal inheritance is much easier when you can ‘create’ magic copies of objects at will. Wouldn’t it be nice if JavaScript had the ‘create’ function like we did in OolongScript? I give you my slightly simplified version of Douglas Crockford’s ‘create’ function:
var create = function(o) {
var F = function () {};
F.prototype = o;
return new F();
};
I leave it as an exercise to the reader to figure out how this works.
Hint: replace ‘F’ with ‘Jet’ from the example above and ‘o’ with ‘biplane’.