Beware of the prototype properties

December 14, 2010

First of all I’m sorry that I haven’t wrote for a long time, I’ve been busy, but since I realized that is never going to change (at least not before the kids will move out of the house) I better continue write post even when I don’t have time. So I guess the post will be shorter and maybe less esthetic(usually it means more English mistakes), but it’s better than nothing, right?

So, a few updates.

1) I released a firefox addon – web-exposed – you should check it out
2) I did a very cool event with Israeli & Palestinians – Startup Weekend Tel-Aviv. Here is a cool video we did.
3) I started a new job which required relocation to Frankfurt Germany. I joined Crytek to their web development team and working on a cool social network project.

That’s it for the updates, if you survived so far i guess it can only get better from here (or would it…)

The Prototype

In Javascript each function has prototype properties that refer to an object. Whenever you create a new instance of this class this reference is being set to the new created instance by the javascript engine. Whenever a method is called on the object the javascript engine search the method in the object and if it doesn’t find it, then it searches the prototype chain for this method.
This basic behavior enables javascript inheritance, but this is where it gets tricky, and most developers don’t really know – the prototype is being used by all instances of the function!!!
Let’s review the following code:


function myClass(){}
myClass.prototype = {
//declaring some public methods
	method1: function(){alert("method1");},
	method2: function(){alert("method2");},

	//declaring some public properties
	myName: "some name" ,
	myArray: [1,2]
};

Do you know what’s wrong with this code?
Let’s run some code and see the bug that hides in this declaration:


m1 = new myClass();
m2 = new myClass();

alert( m1.myName );
alert( m2.myName );
m1.mtName = "i changed the name";
alert( m1.myName );
alert( m2.myName );

If you ran the code or can guess what happen you won’t notice anything strange. At the beginning both instances have the same value and after m1 changed the myName m2 still have the correct value.
so far so good. Now let’s examine the following code:


alert( m1.myArray.toString() );
alert( m2.myArray.toString() );
m1.myArray.pop();
m1.myArray.pop();
alert( m1.myArray.toString() );
alert( m2.myArray.toString() );

FAIL!
the m1 instance changed the value of the array of the m2 instance, or any other instance for that matter.

Asymmetric Prototype I/O

Since each function has the same prototype object and there could be many instances using this object, the javascript engine enforces asymmetry between reading and writing property value.
When you set a property in the prototype all the instance can read it. once an instance try to write it, javascript creates a local copy of that property and set the value there, so whenever it reads again it will read from the local copy.
This is why our first example worked. The m1 instance changed the myName property and javascript created a local copy with the new value while m2 was still reading from the prototype object.

In the second example we did not set the array but manipulated it, which changed the array object for all instances. If you would have done something like this:


m1.myArray = new Array();

then the new array would have been set only for m1 instance.

If you are not aware of this asymmetric behavior then these kinds of bugs are bound to happen and usually they are hard to track since object data is being changed in instances without you knowing about it – very hard to debug.

My recommendation is to avoid properties in the prototype what so ever. I only set properties in the constructor:


function myClass(){
    this.myArray = [1,2];
}

Don’t say I didn’t warn you.

Bookmark and Share
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • E-mail this story to a friend!
  • FriendFeed
  • LinkedIn
  • StumbleUpon
  • Twitter

tags: , ,
posted in Blog by Amir Harel

1 Comment to "Beware of the prototype properties"

  1. Sander Aarts wrote:

    The problem occurs because you initialize myArray in the prototype object instead of in the constructor function itself. Therefore all instances reference the same array.

    If you'd change myClass to:

    function MyClass(){
    this.myName = “some name”;
    this.myArray = [1,2];
    }
    MyClass.prototype = {
    //declaring some public methods
    method1: function(){alert(“method1″);},
    method2: function(){alert(“method2″);}
    };

    … it would work as expected, although you'd have to change myClass to MyClass in all your references as well (capitalizing constructors is good practice). This way each instance will have its own array.

    BTW you misspelled myName in 2nd script as mtName.

 
Powered by Wordpress and MySQL. Theme by openark.org