Room51 Technical Documentation

JavaScript Prototypes - Placement

Introduction

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?

Initial Function

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.

Prototype Properties

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

Setting the Prototype Before Instantiation

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

Switching Prototypes

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

Prototypes in the Constructor

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