Home > Programmer > JavaScript Prototypes and Inheritance

JavaScript Prototypes and Inheritance

December 20th, 2010 Leave a comment Go to comments

Someone on Hacker News asked for a concise description of “prototype” in JavaScript. I gave it a go, but the topic closed before I could post. So I saved it here for posterity.

And then I blew it out and made it considerable less concise, which might or might not be for the best, but you can be the judge of that.

Prototype-based inheritance in JavaScript can be a little bit strange to people accustomed to class-based inheritance, but it really doesn’t need to be difficult to grok. Like so many things, once you “get it”, it seems easy enough.

Before we begin, let’s do a small bit of terminology. In JavaScript, an object’s “constructor” is itself an object. For example, you might have seen something like this:

function Kitten() { this.fuzzy = true; }

var k = new Kitten();  // "Kitten" is k's "constructor"

But if you look, you see that Kitten is an object, right? It’s an instanceof a Function, in particular:

js> Kitten instanceof Function
true

Since Kitten is an object, it means Kitten can have properties, such as Kitten.prototype.

Just hold onto that for a moment; we’ll come back to it.

Here’s a tricky one: an object’s prototype is an implicit reference to the value of object’s constructor’s prototype property. That is, the object’s prototype is the object pointed to by its constructor’s prototype property.

Even saying it two different ways doesn’t really seem to take the edge off, does it?

Let’s look at the Kitten example, above. In it, k‘s constructor is Kitten. Therefore, k‘s constructor’s prototype property is Kitten.prototype. Therefore, by the above definition, k‘s prototype is Kitten.prototype.

Tersely, and only slightly incorrectly: k is-a Kitten, so k‘s prototype is Kitten.prototype.

(The “object’s prototype” is different than the “object’s prototype property”! I know! Remember, the object’s prototype is its constructor’s prototype property! This is a zany terminology thing that can really get confusing.)

So when you construct an object, that object gets an implicit reference to the value of the constructor’s “prototype” property, which is an object:

js> Kitten.prototype
[object Object]

Now, by “implicit reference”, I mean that it’s an internal thing, not some property on your object. In the Kitten example, k.prototype is not the same as Kitten or Kitten.prototype. The implicit reference is hidden away. (It can, however, be gotten with Object.getPrototypeOf().)

Finally, Here’s The Meat: If a property cannot be found in an object, it is searched for in that object’s prototype.

That’s basically it.

All the rest of it is neato emergent behavior.

Simple example:

// here's the constructor:
function Weasal() { }  // Weasel is to Weasal as Kernel is to KERNAL

// set up the prototype object to have some values:
Weasal.prototype = { x: 10, y: 20 };

// or you could do this:
Weasal.prototype.z = 30;

// make a new Weasal object
// (this object gets an implicit reference to Weasal.prototype object)
var frank = new Weasal();

frank.x == 10; // TRUE -- from Weasal's prototype
frank.z == 30; // TRUE -- from Weasal's prototype
frank.hasOwnProperty('x'); // FALSE -- it's in Weasal's prototype object

// now check this out:
frank.x = 30;  // assign a value to frank's x property
frank.x == 30; // TRUE -- but now we're looking at frank's x!
frank.hasOwnProperty('x'); // TRUE -- Weasal's prototype x is hidden

// and then:
delete frank.x; // blow away frank's x property
frank.x == 10; // TRUE -- it's looking at Weasal's prototype again

Here’s an example involving making “methods” on an object:

function Shark() { }; // constructor

Shark.prototype.fireFrickinLaserBeam = function() { doit(); };

fred = new Shark();

// fred doesn't have a fireFrickinLaserBeam property, so it is looked for
// in its prototype object (Shark.prototype) and runs from there:

fred.fireFrickinLaserBeam();

And here’s a more complex example involving inheritance:

function Legume() { }
Legume.prototype.health = 20;

function Bean() { }
Bean.prototype = new Legume(); // inheritance!  See below for discussion
Bean.prototype.audio = true;

var b = new Bean();
b.goats = 3490;

Take a look at that code, and see how it compares to the following diagram, which shows the objects and their properties:

Four objects and their properties. The dotted lines represent the implicit connection to the object's prototype (put obtusely, the dotted lines represent the implicit connection to the object referred to by the object's constructor's prototype property.)

At this point, b only has the goats property … so you if reference b.audio, it looks for it in its prototype object (Bean.prototype)and gets it there, just like in the previous examples.

This is a specific case of our original general rule: if an object doesn’t have the property you’re looking for, the property is looked for in that object’s prototype object.

Now for the magic: you might have noticed that the word “object” appears at the beginning and end of that rule… the rule applies to both the object and the object’s prototype because the object’s prototype is an object.

This means that if the property isn’t found in the prototype object, it is looked for in the prototype object’s prototype! And turtles all the way down the “prototype chain”.

To run the example, then, when we try to evaluate b.health:

  1. Is health in b? No. So…
  2. Since b‘s prototype is Bean.prototype, is health in Bean.prototype? No. So…
  3. Since Bean.prototype‘s prototype is Legume.prototype, is health in Legume.prototype? Yes! Bing! We got it!

In this way, you can build an inheritance tree by chaining prototypes to other objects, just like we chained Bean.prototype up to a Legume object. We could make a further subclass Pinto, and set its prototype to a Bean object, for example.

Setting the constructor property

One thing that I didn’t mention is that prototype properties of Functions have a constructor property. This property references the function itself, i.e. the constructor. For example:

function Beet() { } // constructor
Beet.prototype.constructor == Beet;  // TRUE

// now we can conveniently get the constructor of an object through the
// established magic of the prototype chain:

var b = new Beet();
b.constructor == Beet; // TRUE, from Beet.prototype

However, this gets hosed when you do inheritance. Here’s the breakage, just so you understand it:

function Goat() { } // constructor

function UltraGoat() { } // constructor
UltraGoat.prototype = new Goat(); // inherit from Goat

// Goat.prototype is the built-in Function prototype property, which has a
// constructor property referring to "Goat":

Goat.prototype.hasOwnProperty('constructor'); // TRUE
Goat.prototype.constructor == Goat;  // TRUE

// HOWEVER, UltraGoat's prototype is just a normal object created with new. It
// doesn't even have a constructor property in its prototype property:

UltraGoat.prototype.hasOwnProperty('constructor'); // FALSE (trouble brewing!)

// and so, when you try to get its constructor, it goes up the prototype
// chain to Goat.prototype!
UltraGoat.prototype.constructor == UltraGoat; // FALSE, Aghh!!
UltraGoat.prototype.constructor == Goat; // TRUE, wrong!!

Fortunately there is a quick fix. Now, if you never reference your Ultra-Goat’s constructor, this won’t be a problem. But nevertheless people commonly patch up the constructor property when inheriting, like so:

function Goat() { } // constructor

function UltraGoat() { } // constructor
UltraGoat.prototype = new Goat(); // inherit from Goat
UltraGoat.prototype.constructor = UltraGoat; // fix the constructor property

// make some goats:
var goat = new Goat();
var ultraGoat = new UltraGoat();

// and now this will work properly:
goat.constructor == Goat; // TRUE, as expected
ultraGoat.constructor == Goat; // FALSE, as expected
ultraGoat.constructor == UltraGoat; // TRUE, as expected

There’s more fun to be had with prototypes, but you’re a lot of the way there, now.

Share
  1. JP
    January 1st, 2011 at 15:20 | #1

    Hey – this is the first step in the right direction for me to actually understand how to get the prototype chain set up! I think what I learned is, The prototype chain is a chain of implicit prototypes and you cannot set an object’s implicit prototype. (Losing confidence as I type…. sigh.) That is why you have to set your subclass’s constructor’s prototype to an instance the superclass, which itself contains an implicit reference to its prototype.

    I am still fuzzy on some details, however …

  2. Aaron
    February 6th, 2012 at 20:06 | #2

    Nice post. Definitely helps me understand JS inheritance a little better. Are you familiar with Crockford’s “create” function?

    mynamespace.object.create = function( o ) {
    function F(){};
    F.prototype = o;
    return new F();
    };

    This tends to forego the constructor and prototype chain altogether. I am assuming these two approaches (the one you mention in your post vs. Crockford’s) are mutually exclusive of each other? I suppose DC’s method is only really useful for inheriting the properties of object literals.

  3. February 7th, 2012 at 10:52 | #3

    @Aaron I have seen that, and it’s not really mutually exclusive. I don’t like it because it hides the “JS Way” too much for my tastes. A friend of mine who is a JS expert uses something like it, though, and I can respect that.

    As a JS developer, I should read this:

    Bar.prototype = new Foo();

    automatically as:

    “Bar inherits from Foo” or “Bar is subclassing Foo”.

    So we can either:

    Bar = function() {};
    Bar.prototype = new Foo(); // my preferred way
    barInst = new Bar();

    or:

    barInst = subclassThenInstantiate(new Foo()); // Crockford’s

    As such, I don’t feel that Crockford’s method, however valid, provides any additional clarity to the situation.

    Also in his method, the “class” Bar is hidden away, never to be seen again. This might not be a practical issue, but it makes me uneasy.

    Plus, understanding his method of prototypal inheritance is dependent on understanding closures, and those are two bites for beginners where perhaps there should only be one. (This point relates to teaching, rather than the validity of either method of doing things.)

    Again, Crockford’s method is A-OK. It’s just not my preferred. :-)

  1. No trackbacks yet.
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)

What is 8 + 2 ?
Please leave these two fields as-is: