Beej's Bit Bucket

 ⚡ Tech and Programming Fun

2010-12-21

JavaScript Prototypes and Inheritance

JavaScript Logo

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:

Inheritance Chain 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.

Historic Comments

 JP 2011-01-01 23:20:37

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 ...

 Aaron 2012-02-07 04:06:58

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.

 beej 2012-02-07 18:52:43

@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. :-)

 Michael Hall 2012-03-20 18:38:28

thanks so much for this post, i really got confused when i read crockford's js the best parts when he showed that object constuctor function but now i finally understand that he was simply using a function to 'more or less' automate the creation of a new object and then immediately correct the constructor so that it points to itself rather than it's parents constructor.

i have never really used the constructor property, only the prototype property, but i can see now how it can be used to 'lookup' which object was used as the prototye(or class) that new object is created/templated/instantiated from, but because the new object automatically inherits its parent's constructor property, you can't ask any objects made by the child who constructed it unless you change also change the sub-class constructor to point to itself not it's parent(which it inheritted).

but does this mean that you can't ask the child class of the original parent who it's constructor is anymore after you tell it to point to itself? i guess it doesn't matter since
you can ask who it's prototype is instead? i guess what i'm asking is can you look up any objects prototype as well as it's constructor, or is the objects prototype just gonna return 'object object' whereas the constructor returns the 'name' of the actual constuctor?

can you do another article or elaborate on when it would be useful to ask the object what it's prototype is compared to asking what it's constructor is, or does the prototype always return 'object object'.

i understand that the first object to be created, lets call it First(){}, would have a prototype of 'object object' but what if made a new child object named Second{} and set Second's prototype to new First(), should Second's prototype == First, i know Second's constuctor == First, but is Second.prototype == First or Second.prototype == [object Object].

i guess i should just try it out huh, lol. anyways when is it useful to look up the prototype versus the constructor

 beej 2012-03-20 22:47:22

@Michael Hall Second.prototype == [object Object], since "new First()" produces an object. "First" is a function.

If you want to keep track of the superclass, it's definitely possible to explicitly do it:

Second.prototype = new First();
Second.prototype.constructor = Second;
Second.prototype.super = First; // just make up a "super" property

Then you can even call it:

function Second() { // constructor for Second
// call super constructor
this.super.apply(this, arguments);
}

 Michael Hall 2012-03-21 19:22:38

ah that is so cool and clear, since functions *are* objects the prototype of all objects constructed by a function would return [object Object] which i believe is the base class of all newly created functions and native functions that are shipped with javascript.

so above the first line is assigning all Second objects to refer/use First() as their parent and therefore will inherit from First()

then, the second line corrects the 'pointer' so that Second objects 'know' that they are constructed by second instead of their inherited constructor value which comes from or would have been 'First' in this case

and the 3rd line is explicitly(or manually) creating a super property and setting it to the 'name' of the function that acts as it's parent. i assume then, based on the suggestion of manually tracking the parent in a manually created 'super' property and based on the 'prototype based programming' article at wikipedia linked to at the beginning, that javascript does not automatically keep track of the super class?

 beej 2012-03-22 08:01:25

@Michael Hall Completely correct after the first paragraph, and no, JS does not track the super. The first paragraph is just a bit raw, so I'll flesh it out a bit to make sure I'm understanding.

For clarity, let's say [[Prototype]] is the internal prototype pointer that is followed when doing property lookups. And let's say .prototype is the prototype property on an object.

The .prototype property for functions is just a regular object.

The internal [[Prototype]] pointer on regular objects is initialized to point back to the .prototype property on the function that was used to create it.

function First() {};

First, a Function object, is created with a .prototype that is a regular object with one field: .constructor (set to First). (Function objects have rules for creation that aren't the same as those for other object types.)

x = new First();

x, an empty Object object, has no .prototype. But its [[Prototype]] points to First.prototype.

function Second() {};

Now, like First, Second has a .prototype with a .constructor (set to Second).

Second.prototype = new First();

And now we've blown away that original .prototype, and replaced it with an instance of First. The .constructor is just gone, "new First()" is just returning an empty object.

So now if we say:

z = new Second();

z is an empty Object object, and has no .prototype. But its [[Prototype]] points to Second.prototype.

and we hunt for z.constructor, we look in:

1. z (not there)

2. z's [[Prototype]] AKA Second.prototype (not there—we blew it away)

3. Second.prototype's [[Prototype]] AKA First.prototype (there it is!)

and we find .constructor there, in First.prototype. And it's First. Which is "wrong", since z is clearly a Second.

Our fix was to recreate the .constructor in Second.prototype after we'd blown it away, so that it would be found at Step 2, above.

Put another way, what we're really doing is this:

function First() { };
function Second() { };

var savedConstructor = Second.prototype.constructor; // Save it!
Second.prototype = new First();
Second.prototype.constructor = savedConstructor; // Restore it!

It's just that we *know* that in the above example savedConstructor will *always* be Second, so we just write Second.

Comments

Blog  ⚡  Email beej@beej.us  ⚡  Home page