Prototypes in JavaScript
From Spheriki
This article uncovers the bits about prototypes in JavaScript that you may not have known. Some knowledge of basic JavaScript object-oriented programming is assumed. Basic prototype chain look-up is also assumed.
Contents |
The 3 principles of prototypes
Here they are:
- prototype is a property of "constructor" functions.
- The prototype property of constructor functions is used (and ONLY used) when the new keyword is used to make a new object.
- The actual prototype of objects themselves is hidden and internal.
Constructor functions
prototype is a property of "constructor" functions.
Any function can be a constructor function. Here's one now:
function Person(name, age) { this.name = name; this.age = age; }
It's a function just like any other. It's also an object in JavaScript, which means I can muck around and give it properties:
// The Person function smells terrible! Person.smell = "terrible!";
That doesn't do much. Here's something that's more important:
Person.prototype = {};
prototype is a property, just like that smell. smell happens to be a string, and prototype happens to be a blank object.
Guess what? Setting that prototype property of the Person function to a blank object is exactly what JavaScript does for you! This is why you can get away with this:
Person.prototype.yell = function (message) { Say(message + "!!!"); };
The blank object is already there in prototype, so you can add whatever you feel like to it. Usually what you add are methods.
There is nothing special about the prototype property. At least, not yet.
Making objects
The prototype property of constructor functions is used (and ONLY used) when the new keyword is used to make a new object.
Normally, the prototype property of a function sits quietly and does nothing. But it is used in exactly one place.
The new keyword!
Let's make a person:
var bob = new Person("Bob", 40);
What just happened here?
- A new blank object is made.
- The prototype link is set for the new object using the prototype property of the Person function.
- Constructor function runs. That blank object is referenced via the this keyword.
- The object is passed back to the bob variable.
That second step is the important bit. The newly-made object doesn't have any methods, but if it looks up its prototype link, it will find the yell method. But we all knew that, right?
Well there's prototypes and there's prototypes
The actual prototype of objects themselves is hidden and internal.
The following is an undefined value:
bob.prototypeThen where the heck is the "prototype link" stored?
The official answer is that it's unreachable: there is no way to check, test or set the prototype of an object.
The SpiderMonkey answer is that you can get it via the special, non-portable and internal property named __proto__, like so:
bob.__proto__
These are equal:
Person.prototype == bob.__proto__At least, they're equal if you don't change either of them. You see, the setting of the __proto__ property is as described in the last section: it only happens when you use new. They won't magically stay in sync.
Try to avoid setting __proto__ if at all possible. My experiences have only led to frustrating experiences with cyclic references (Sphere's embedded SpiderMonkey implementation will usually pitch a fit.)
Anyway, keep these two facts in mind:
- Constructor functions have prototype.
- Constructed objects have __proto__, which you generally shouldn't mess with.
Inheritance in depth
Here's a typical example of inheritance in JavaScript:
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.yell = function (message) { Say(message + "!!!"); }; function Soldier(name, age, weapon) { Person.call(this, name, age); this.weapon = weapon; } extend(Soldier, Person); Soldier.prototype.yell = function (message) { Say("Sir! " + message + ", sir!"); };
Where extend looks like:
function extend(child, supertype) { child.prototype.__proto__ = supertype.prototype; }
It looks hackish, but this is how Mozilla recommends you do it. Anyway, what is it really doing?
- The Person function is defined. Its prototype property starts off as a blank object.
- The prototype of Person is given a yell method.
- The Soldier function is defined. It also has a blank object for its prototype property.
- The prototype of the Soldier's prototype (kinda like Soldier's Grandpa) is set to the Person's prototype (kinda like Person's Dad).
- For kicks, the prototype of Soldier is given its own yell method, which occurs lower in the prototype chain, and therefore will be used for Solider objects.
Often, you'll see this pattern instead:
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.yell = function (message) { Say(message + "!!!"); }; function Soldier(name, age, weapon) { Person.call(this, name, age); this.weapon = weapon; } // JavaScript allows calling our function // with no arguments if we want to. Soldier.prototype = new Person(); Soldier.prototype.yell = function (message) { Say("Sir! " + message + ", sir!"); };
This does a similar thing to the above, but it also runs the Person constructor function.
This is popular. It's also discouraged by the Mozilla devs, reason being that calling the constructor to Person is totally unnecessary, and potentially expensive. You have been warned.
Personally, I think you can get away with it as long as your constructor functions don't break with undefined argument values, but just be aware of the consequences, and why people bother doing that in the first place.
Wrap up
The official documentation makes this stuff harder to understand than it should be. Hopefully, you'll now be less confused when somebody starts talking about prototypes in JavaScript.

