Saturday, October 30, 2010

Javascript prototype property

Today, in this post, I am going to discuss about the prototype property of classes in javascript. I came across this property when i was trying to learn about classes in javascript. Javascript, being a dynamic language, allows you to have a number of bizarre yet interesting features that you don't find in mainstream languages. the prototype property is one such feature. Due to its idiosyncrasy, it is usually a source of confusion. I am just going to brush on the basics here so that it becomes easy for us to spot loopholes in the more complex code that we usually write.

Here are our objectives.

  1. Learn where and why the prototype property is usually declared in javascript.
  2. Learn how to use the prototype property in javascrpt.


First, lets try to explain why the prototype property is used in javascript.
The prototype property comes in handy when you have a class and you have already created a set of objects and at a certain point of time, you want to introduce a new property in each of your objects.

Lets see with an example. First we create a class called Person with a property called name

function Person(name){
      this.name = name;
      this.getName=function(){return this.name;};
      
      this.setName=function(nameArg){
       this.name=nameArg;
      };
     }

Then we create a few objects of this class as follows.


var p1= new Person("person1");
var p2= new Person("person2");


Now suppose that need to add a property called 'age' for all the person objects that are currently created. The first way that might come to our mind is to modify the class declaration and add a new property called age to it in the same way that we added name property. But what if the class Person is declared in some common file and is referenced by different scripts, and my requirement necessitates that the new property should be available only for the currently executing script. This is where the prototype property comes to the rescue.

We can add a property to all the instances of the class by writing the following lines of code.

var p1= new Person("person1");
     var p2= new Person("person2");
     
     Person.prototype.age=30;
     
     console.log(p1.age);
     console.log(p2.age);


As seen in the above code, when i print the value of the variable age, both the objects are able to print the new property.

An interesting thing to not here is that the property called age that has been newly created here is actually a class variable. i.e. all the objects created are able to see the same value of the newly added property. What this means is that if we try to modify the property via one variable, all the objects will see the new value of the property. Since objects are equivalent to functions in javascript, the prototype property can be used to create new functions on the class as well.

Lets modify the code for the Person class by adding a function to set the age.

this.setAge=function(argAge){
       Person.prototype.age=argAge;
      }

So far so good.

Now, lets see what happends when we print the age of the objects after modifying the age for one of the objects.

Person.prototype.age=30;
     Person.prototype.setAge=function(argAge){
       Person.prototype.age=argAge;
     }
     
     
     console.log(p1.age);
     console.log(p2.age);
     
     p1.setAge(40);
     
     console.log(p1.age);
     console.log(p2.age);


If you run this, you would see that both the objects p1 and p2 have the same value of age after being modified. So, after all, what was the use of creating a new property if it is being shared across all the objects. Also, we saw that in the setAge function that we are using the fully qualified syntax to access the age property. Lets see what happens if we use the shorthand notation in the setAge method.

Person.prototype.setAge=function(argAge){
       this.age=argAge;
     }

     console.log(p1.age);
     console.log(p2.age);
     
     p1.setAge(40);
     
     console.log(p1.age);
     console.log(p2.age);

Now, if you were to print the age of the two objects, they would differ. Seems like our problem is solved. But what actually was the cause of the problem in the first place? The problem arises due to the way in which the javascript engine searches for the age property on the object.

When trying to access the age property, the engine will first search the property on the instance of object. i.e. this.age. If this property is not available on the object then the engine will search for the property in the prototype property. Since the variable 'age' is declared on the prototype, it is found and printed on the console when we try to print it for the first time.

But when we set the property using the setter method using the "this.age=argAge", a new property is created on the current instance and its value will be set to the value passed as the argument.

You might be wondering, what is the use of creating a property using the prototype method. The main advantage that you get by using the prototype property is that you can create new class level properties or functions at runtime without modifying the class. And from then on you can read the value of this property from all the instantiated objects. You can then also create a property of the same name on the objects using the value of the prototype property.

For example

Person.prototype.addAge=function(){
      this.age=this.age+1;
     }

p2.addAge();
     
     console.log(p2.age);


In the function addAge, the "this.age" on the RHS will refer to the Person.prototype.age the first time it is called and the "this.age" on the LHS will create a new public property called "age" on the current instance. Further invocation on the method will cause the newly created publicly created property.

Thats all for now folks!

Happy Programming :)
Signing Off
Ryan

No comments: