Just JavaScript - The Prototype Mechanism
Written by Ian Elliot   
Friday, 05 December 2014
Article Index
Just JavaScript - The Prototype Mechanism
Extended role of the constructor
Object factories and prototype

Improve efficiency using a prototype

Where you do get an efficiency gain is when you supply a function as a property using a prototype.

For example:

var myProto = {a: 1, b: 2, c: 3,
       myDisplay: function() {
         console.log(this.a);
 }};

Now the prototype has a method that displays its own property. 

 

var myObject1 = Object.create(myProto);
var myObject2 = Object.create(myProto);
myObject1.myDisplay();
myObject2.myDisplay();

Now both objects display the value 1 but they both make use of the same definition of myDisplay which is only stored in the prototype object.

In fact this is the main problem that the prototype mechanism was invented to solve. In class-based languages methods that are defined in the class are not copied to every instance of the class - only the data is copied. That is, in class-based, object-oriented programming all instances share the common code of methods but have their own data.

The prototype mechanism in JavaScript allows you to share a single implementation of methods between a set of objects.  

All you have to do is set up the prototype object with all the shared methods and then set this as the prototype for all the objects you create. 

Of course, if one of the objects defines its own method with the same name then the prototype method is shadowed. 

There is also the fact that you can use multiple prototype objects via the prototype chain.

To a programmer familiar with class-based objects this looks a lot like either inheritance or multiple inheritance depending on whether or not the prototype objects form a natural hierarchy - i.e. does each one "extend" in some sense the previous one. 

There is one final subtle point concerning the use of this.

In the definition of the prototype:

var myProto = {a: 1, b: 2, c: 3, 
       myDisplay: function() {
         console.log(this.a); 
 }};

 

we have a reference to this.a. What does this reference?

The good news is that it follows the usual logic. The this is set to the calling context. So if you call:

myProto.myDisplay();

this is set to myProto.

If you call:

myObject1.myDisplay();

this is set to myObject1 but myDisplay isn't an own property and so it is resolved by the prototype chain, i.e. myProto supplies the function. The this still references myObject1 but when myDisplay tries to access myObject1.a it can't find it and so the prototype chain is used again and this resolves to the a supplied by myProto.

Now let's shadow the a property by creating an own property:

var myObject1 = Object.create(myProto);
var myObject2 = Object.create(myProto);
myObject1.a=10;
myObject1.myDisplay();
myObject2.myDisplay(); 

The first call to myDisplay has this set to myObject1 and myDisplay is supplied by myProto but myObject1.a exists as an own property so it displays 10. The second call works in the same way but myObject2.a isn't an own object and so is supplied by myProto. 

The rule is that this is set to the calling context of the object and any methods supplied by the prototype object are executed in this context. 

A Simple Example

If your object is a singleton, there will only ever be a single instance of the object, so you often don't need to worry about prototypes. 

In a typical JavaScript program most of the objects are singletons - the main exception being objects designed to store data. In this case there is often a lot to be gained from using a prototype.

For example, a typical 2D point object might be defined as: 

var point1 = {x:0, y:0,
   setxy:function(x, y) {
      this.x = x;
     this.y = y;
   }};

and a typical use might be:

point1.setxy(10,200);
console.log(point1.x,point1.y);

If you want a second point you would define the whole thing over again  - probably using an object factory or a constructor rather than manually:

var point2 = {x:0, y:0, 
   setxy:function(x, y) {
      this.x = x; 
      this.y = y;
   }};

If you wanted 5000 point objects then having the setxy function defined 5000 times, one for each instance is a little wasteful. 

A better solution is to set up a prototype object with the methods that a point object needs.

var pointMethods={
     setxy:function(x, y) {
      this.x = x;
      this.y = y;
}};

You could include the intial data variables x, y if you want to, or they could be created on the instances as own properties - it doesn't make a great deal of difference. 

Now you can create two point objects very easily:

var point1 = Object.create(pointMethods);
var point2 = Object.create(pointMethods); point1.setxy(10,200);
console.log(point1.x,point1.y);

Notice that in this case the call to setxy creates the two own properties x and y for us. In general it is better to create any variables either as own variables or as part of the prototype. 

The Extended Role Of the Constructor

If you have followed the idea of the prototype object and the way it provides a delegation mechanism for any properties that are not defined on an object, be reassured that it really is this simple. However, there is a slightly more complicated way of setting an objects [[prototype]] property that was designed early in the life of JavaScript to provide something that looked like a class-based type and inheritance mechanism. 

A constructor is just a function that is called with the new operator and returns an object. The idea is that the constructor is a special sort of object factory. 

JavaScript takes this idea one step further and for some it is a step too far. 

The idea is that a constructor can be used to create many instances of an object. This is a bit like the way in class-oriented languages a class can be used to create many instances of an object. 

So a constructor can play the same role as class in that it weakly defines a "type" of object. 

Put simply, the assumption is that if a constructor creates lots of instances of an object then they can be regarded as being of the same type. In this sense the constructor plays the role of a class and it defines an object type. 

This isn't unreasonable in the sense that any object that a constructor creates has the same set of properties as all the others it creates and what properties an object has defines its type. 

If a constructor is so important in the life of an object, why not allow it to play a role in the setting of an object's prototype chain. This is again part of thinking of the constructor as if it defined a class - complete with a hierarchy of classes it inherits from.

The constructor prototype mechanism is:

  • Every function object has a prototype property.
  • This is not the internal [[prototype]] property and it doesn't reference the function's prototype chain this is, like every object determined by the functions internal [[prototype]]
  • When a function is called with the new operator, i.e. as a constructor, the object it creates has its [[prototype]] property set to the constructor's prototype property.
  • That is, the function's prototype property sets the prototype chain of the object it creates.

This makes sense if you want to regard the constructor as playing the same role as a class.

Every object that the constructor produces has the same set of properties and the same prototype chain.  

As an example of this approach to setting the prototype we can reimplement the 2D point object.

We can leave the definition of the point prototype object as it was:

var pointMethods={
     setxy:function(x, y) { 
      this.x = x; 
      this.y = y; 
}};

 

Now we are going to use a constructor to create a point object:

function Point(){
 this.x=0;
 this.y=0;
}
Point.prototype=pointMethods;

Notice that now we have set the constructor's prototype property to the pointMethods object. Again it is important to stress that pointMethods is not the function's prototype. It will be the prototype object for any objects the constructor creates. 

For example:

var point1 = new Point();
var point2 = new Point();
point1.setxy(10,200);
console.log(point1.x,point1.y);

Now both point1 and point2 have pointMethods as their prototype. 

You can, of course create the prototype object by direct assignment to the prototype property, and this is a very common idiom, but it suffers from not automatically collecting all of the instructions that create the prototype in one place. For example you could write:

Point.prototype.setxy= function(x, y) {
      this.x = x;
      this.y = y;
};

and build up the prototype object one property or method at a time. 



Last Updated ( Sunday, 10 May 2015 )