Prototype context
Article Index
Prototype context
Solution

The prototype mechanism is a minefield of problems waiting to trap the ambitious JavaScript programmer especially so when combined with a serious object oriented approach. We tackle the puzzle of the missing private variables.

 

JavaScript is a wonderful language, but if you don't understand it well enough it can be cruel. This puzzle is a tale of how a well-meaning, fairly advanced, programmer got stuck by thinking that JavaScript's approach to objects was just like all the other class based languages.

Background

One of the problems in learning JavaScript is mastering the prototype mechanism. It is often put forward as the key to JavaScript object-orientation and inheritance in particular - it isn't. The prototype mechanism is just a way of making things more efficient. If you create an object by defining a constructor function then every variable and every method is an instance variable or method. That is if you define a constructor function as:

MyConstructor = function(){
this.y="XYZ";
this.getY1=function(){
return this.y
}
}

then you have created an instance variable y and an instance method getY1. Every time you create an instance of the object you get a complete copy of y and a complete copy of getY1:

MyObj=new MyConstructor();
alert(MyObj.getY1());

This seems very reasonable for the instance variable as each instance needs to store its own data in its own version of y. You can't share a variable between instances unless it always stores the same value for all instances. However, making a complete copy of the method for each instance seems a waste as you are simply copying the same instructions over and over again. In this case a shared copy of the code would do the job just as well and this is where the prototype mechanism comes into play. Any property that you create on the constructor's prototype object is used if an instance created by the constructor doesn't have that property. So the prototype object acts as a sort of repository of shared properties. In most cases it only really makes good sense to share methods in this way but you can share properties that are simple values and even entire objects.

So for example a more efficient implementation of the getY method would be:

MyConstructor.prototype.getY2=function(){
return this.y;
}

Now when you write:

MyObj=new MyConstructor();
alert(MyObj.getY1());
alert(MyObj.getY2());

MyObj has its own copy of the getY1 method but when you use getY2() it is the version provided by the prototype that is used. If you have 1000 instances of the object then there are 1000 copies of the code in getY1() but only one copy of the code in getY2().

Yes the prototype mechanism can be seen as a way of implementing inheritance but it really is all about efficiency.

Banner

Puzzle

Now we come to our eager JavaScript programmer wanting to put object-orientation into effect. As with all programmer puzzles the situation has been stripped down to is bare essentials. In the real world the code did a good job of hiding the problem.

Essentially what was needed was an object with a private variable implemented in the usual way via a closure in the constructor. That is:

MyConstructor = function(){
var x = "ABC";
this.getX = function(){
return x;
}
}

The variable x can be accessed by the member methods of the object but not from outside of the object. That is you can write:

MyObj=new MyConstructor();
alert(MyObj.getX());

and see the current value stored in the private variable x for that instance of the object. However, if you try to use something like:

MyObj.x="XYZ";

then it will fail because MyObj doesn't have a property called x - hence x is a private variable.

After implementing the class represented by our simple example above our enthusiastic object-oriented programmer remembered being told that it was a good idea to move all methods into the prototype object so as to create a more efficient implementation. So the object constructor was changed to:

MyConstructor = function(){
 var x = "ABC";

and a lot of other definitions

}

MyConstructor.prototype.getX=function(){
return x;
}

When the new version was tried out some methods still worked as they did before the change, but some failed occasionally and the extreme example shown in our example i.e. getX failed all the time. That is:

MyObj=new MyConstructor();
alert(MyObj.getX());

resulted in nothing being displayed.

What is going on?

What exactly has failed and can it be fixed?

Turn to the next page when you are ready to find out.

Banner

<ASIN:0596805527>

<ASIN:0470684143>

<ASIN:0137054890>

<ASIN:0596517742>