The chain of prototypes
It's quite simple indeed. Each object has a reference to a prototype , which is always another object, or null
. This object, in turn, also has a prototype. A prototype chain is then formed.
And what's the use? The operation is similar to the concept of inheritance. Consider the following example: you have an object that represents a vehicle, let's call it veiculo
. Assume yet two other objects, carro
and onibus
, that have veiculo
as a prototype. And suppose veiculo
has a buzinar
method. The prototype chain makes it possible to invoke this method in carro
and onibus
:
carro.buzinar();
onibus.buzinar();
And how does this work? When invoking the method, the interpreter checks whether the object in question ( carro
or onibus
) has a method with that name. In this case, objects do not have this method. Then the interpreter checks whether the prototype of each object has the method, and in this case it finds and invokes the buzinar
method. If the method was not found, the interpreter would continue the verification by going up the chain of prototypes until it finds the desired method or property, or until it reaches an object that has null
as a prototype.
It is important to know that this invocation works as if the method belonged to the object itself; that is, any reference to this
within the buzinar
method will point to carro
or onibus
in this example.
Prototype assignment
The language does not offer any official way of changing the prototype of an on-the-fly object. The prototype is assigned only at the time of object creation, and this can be done in two ways.
1. Assignment via constructor
Every function has a property called prototype
(functions are JavaScript objects, so they can have properties). When a function is invoked as a constructor, a new object is created whose prototype is the value of the prototype
property of the function. For example:
function Veiculo() {}
Veiculo.prototype.buzinar = function() {
alert('Fom');
}
var carro = new Veiculo(); // invocação como construtor
carro.buzinar(); // dispara o alert
2. Direct assignment
From ECMAScript 5, the language provides another way to create an object with a certain prototype, the Object.create
function. Note that it is a constructor Object
method, so it can be considered a static method. What this function does is create a new object whose prototype is what was passed as the first argument:
var veiculo = {
buzinar: function() {
alert('Fom');
}
};
var carro = Object.create(veiculo); // veiculo será o protótipo de carro
carro.buzinar(); // dispara o alert
3. Attribution on-the-fly
Officially, JavaScript does not allow changing the prototype of existing objects, but major implementations (including all browsers I know of) allow this through the non-default property __proto__
. Using this property you can do something like this:
var veiculo = {
buzinar: function() {
alert('Fom');
}
};
var carro = {}; // carro ganha protótipo padrão (Object.prototype)
carro.__proto__ = veiculo; // troca protótipo
carro.buzinar(); // dispara o alert
Specification Notes
The above explanation is very informal, and similar to what the language specification describes , also informally. But the description in the specification may seem a little confusing, as there are two things called "prototype": a prototype
property of the objects of type function , and the [[Prototype]] , which is the official name of the internal property of the objects. When the specification uses the term prototype without code formatting or brackets, it generally refers to [[Prototype]], just as I used the term prototype in the above description .
As far as access to the prototype chain is concerned, the crucial point in the specification is the internal function [[GetProperty]] , invoked when you try to access a property of an object. This is a recursive function, which returns the requested property of a given object, whether it is in the object itself or in one of its prototype chain members.