JavaScript allows us to implement prototypal inheritance. We can choose objects to act as prototypes for other objects. What are the mechanisms underpinning automatic prototype assignment on instantiation and how and when can we alter prototypes while a program is running?
We create a new constructor function called Test and instantiate an object, test, using that constructor.
function Test() {}
var test = new Test();
The function Test is assigned a prototype property, Test.prototype, which will point to an empty object as we have not set one explicitly. When test is created it is assigned an internal __proto__ property that points to the same object as Test.prototype.
If we add a new property to the object Test.prototype points to, it will also be visible to test because test.__proto__ points to the same object.
Test.prototype.foo = 'bar';
alert(test.foo); // bar
alert(test.foo) will first look to see if the test object has a property called foo that has been directly assigned to that instance. If it doesn't find one it will look in the object pointed to by test.__proto__.
We can set the object that Test.prototype points to before actually creating any objects using the constructor. Then, when objects are created they can have a set of ready-made properties and methods.
function Test() {}
Test.prototype = {
foo : 'bar',
speak : function() {alert("Speaking");}
};
var test = new Test();
alert(test.foo); // bar
test.speak(); // Speaking
We can change the object that Test.prototype points to after having created objects. The previously created objects will still point to their original prototype objects (their __proto__ properties will not have changed). Any new objects created after the prototype switch will gain the new prototype version.
function Test() {}
Test.prototype = {
foo : 'bar',
speak : function() {alert("Old");}
};
var test = new Test();
Test.prototype = {
foo: 'baz',
speak: function() {alert("New");}
};
var test2 = new Test();
alert(test.foo); // bar
alert(test2.foo); // baz
test.speak(); // Old
test2.speak(); // New
We can access the prototype from within a constructor function. However, care must be taken because the object this points to will have its __proto__ property set before the constructor runs.
Setting properties on the prototype is fine but setting the prototype to a new object will not affect the current object being constructed. In other words, this.__proto__ will still point to the old prototype (which will probably be an empty object if we are in the actual constructor function).
function Test() {
// assign a new property: okay
this.constructor.prototype.foo = 'bar';
// assign a new object: probably bad
this.constructor.prototype = {foo : 'baz'};
}
var test = new Test();
alert(foo.test); // bar
this.constructor.prototype points to the same object pointed to by Test.prototype. When the constructor is first called, by var test = new Test(), test.__proto__ will also be set to point at the same object as Test.prototype.
After the first assignment in the constructor we will have:
alert(this.constructor.prototype.foo); // bar
alert(Test.prototype.foo); // bar
// test.__proto__.foo === 'bar'
alert(test.foo); // bar
The second assignment then switches Test.prototype to point to a new object, {foo : 'baz'}. This will affect any new objects constructed by subsequent calls to new Test(). However, the first object's prototype still points to its original object.
var test2 = new Test();
alert(test2.foo); // baz
alert(test.foo); // bar