====== The JavaScript Prototype ======
This is the first in a series of articles intended as a gentle guide through the realm of JavaScript prototypes. If you’re coming here from a class-based language like Java, PHP, C#, or C++ I suggest you put aside everything you know about how objects and inheritance work in those languages. Try to treat the way JavaScript works as its OwnThing.
===== Context and Motivations =====
You probably know how to create an object literal – say for a person:
var fuzz = {
name: 'Fuzz',
greeting: 'Hello. My name is',
sayName: function () {
console.log(this.greeting, this.name);
}
};
This works fine for one-off objects. If you need to create more than one person, you could create a factory function:
function makePerson(name) {
return {
name: name,
greeting: 'Hello. My name is',
sayName: function () {
console.log(this.greeting, this.name);
}
};
}
var fuzz = makePerson('Fuzz');
var athena = makePerson('Athena');
or a constructor function:
function Person(name) {
this.name = name;
this.greeting = 'Hello. My name is',
this.sayName = function() {
console.log(this.greeting, this.name);
}
}
var fuzz = new Person('Fuzz');
var athena = new Person('Athena');
In both of the above, ''%%fuzz%%'' and ''%%athena%%'' each have their very own copies of ''%%name%%'', ''%%greeting%%'', and ''%%sayName%%''. For a small set of small objects, this is fine. But consider that every Person so defined has properties whose values are common to all Persons (''%%sayName%%'' and, arguably, ''%%greeting%%''). Wouldn’t it be nice if there was a convenient place where we could put shared stuff like this and only refer to that one copy instead of making each object carry around its own copy? This could save a lot of memory if there are a lot of Persons or if a shared property happens to be really big.
Also, what if we want to create a kind of Person that, say, is a bit more specialized – like a Teacher. A teacher might be identical to a Person except that they have an additional ''%%subject%%'' property and a ''%%saySubject%%'' method. Wouldn’t it be nice if we could just specify how a Teacher is special in relation to a Person rather than copy/paste the code we already wrote for Person?
===== Enter the Prototype =====
JavaScript offers a solution to both of these via the **prototype**. Every //function// in JavaScript automatically gets a special property attached to it called ''%%prototype%%'':
function bar () {
};
console.log(bar.prototype);
A ''%%prototype%%'' is an object with a bunch of properties, including, curiously, one called ''%%constructor%%'' that points back to the function itself.
if (bar.prototype.constructor === bar) {
console.log("That's cute.");
} else {
console.log('You lied.');
};
Going further, when you instantiate an //object// it will automatically recieve a property called ''%%__proto__%%'' that is a reference to its constructor’s ''%%prototype%%''. ((The use of the ''%%__proto__%%'' identifier for this function appears to be true of browsers. In Node.js it might be different.))
function Person(name) {
this.name = name;
this.greeting = 'Hello. My name is',
this.sayName = function() {
console.log(this.greeting, this.name);
}
};
var fuzz = new Person('Fuzz');
if (fuzz.__proto__ === Person.prototype) {
console.log("That's cute.");
} else {
console.log('You lied.');
};
If you try to access an object’s property, the JavaScript engine will first look in the object itself for it; if it doesn’t find it there, it will look in ''%%__proto__%%'' (i.e., the constructor’s ''%%prototype%%'') for it.
Let’s set a property on ''%%Person.prototype%%'' and see what happens:
Person.prototype.deepDarkSecret = "I descended from apes."
if (fuzz.hasOwnProperty('deepDarkSecret')) {
console.log('Haz secrets.')
} else {
console.log('No secrets.')
}
No secrets.
console.log( fuzz.deepDarkSecret );
Oops!
The ''%%deepDarkSecret%%'' is actually a property in ''%%Person.prototype%%'', a.k.a., ''%%fuzz.__proto__%%''.
if (fuzz.deepDarkSecret === fuzz.__proto__.deepDarkSecret) {
console.log("That's cute.");
} else {
console.log('You lied.');
};
And when we added it to the prototype, it became available to all instances of Person.
athena = new Person("Athena");
console.log(athena.deepDarkSecret);
===== Not just a cute party trick =====
The upshot of this is that a constructor’s ''%%prototype%%'' is a convenient place to stuff the properties of objects made with it whose values are common to all objects. Most methods fit into this category as well as potentially other properties:
function Person(name) {
this.name = name;
}
Person.prototype.greeting = 'Hello. My name is';
Person.prototype.sayName = function() {
console.log(this.greeting, this.name);
};
var fuzz = new Person('Fuzz');
var athena = new Person('Athena');
fuzz.sayName();
athena.sayName();
''%%fuzz%%'' and ''%%athena%%'' now each have their own ''%%name%%'' properties. However, they are using the common ''%%greeting%%'' and ''%%sayName%%'' properties, saving the memory it would have required for them to have their own copies of those.
When you ask ''%%athena%%'' to say her name, the JavaScript engine first looks for the ''%%sayName%%'' identifier in the ''%%athena%%'' object. But it won’t find it there. So the next place it looks is ''%%athena.__proto__%%'' – which is the constructor’s ''%%prototype%%'' – and it finds it there.
if (fuzz.name !== athena.name) {
console.log("That's what I expeceted.")
} else {
console.log("You lied.")
}
if (fuzz.greeting === athena.greeting) {
console.log("That's what I expeceted.")
} else {
console.log("You lied.")
}
if (fuzz.sayName === athena.sayName) {
console.log("That's what I expeceted.")
} else {
console.log("You lied.")
}
[[#|Next]] we’ll have a look at what happens when you manipulate properties you’ve set on prototypes.