Saturday, November 21, 2009

No one should extend the Object.prototype in JavaScript!

In an earlier post, I wrote:


But everyone should keep in mind that extending the Object prototype in JavaScript is a big NO-NO


But why exactly should no one extend the Object.prototype?

What is the prototype?


Every object in JavaScript has a property (an object) called prototype (Introduced in JavaScript 1.1) that allows you to add custom properties and functions to every instance of that object.

Let's take an example.  First we'll add properties to objects without using the prototype:

var square = function () {  };

var box = new square();
box.height = 55;  //Adding a custom property to the box instance

In the above example, we created a new function called square and other one called box. The box is then initialized as an instance of square.
We then added a custom property to box called height and assigned a value to it.

If we now check the value of box.height, we get 55.

But if we now create another instance of a square, that new instance will not have the height property:

var anotherBox = new square();
alert(anotherBox.height) ; //alerts 'undefined' (not 0)

The reason undefined is displayed is because anotherBox does not have a property called height.

So the best thing to do is to add the height property to the square object, instead of the instantiated objects, via the prototype:

var square = function () {  };
square.prototype.height = 0;

//Now, every instance of square will have a height object

var box = new square();
box.height = 55;

var anotherBox = new square();
alert(anotherBox.height) //alerts 0

anotherBox.height alerts 0 because this time round, the anotherBox object has a property named height and it's set to 0.

I'll soon explain why it's a bad idea to extend the Object.prototype.


In JavaScript, every object inherits from an object called (not surprisingly) Object. Now, earlier on I described how when you attach a property to the prototype, it will exist in all instances of that object...so basically, since every object inherits from Object, when you extend the Object's prototype, every object from then on will have that property that was added to the Object.prototype.

Let's take an example:

Object.prototype.myCustomProperty = 200;

var square = function () {  };
alert(square.myCustomProperty); // 200 is alerted

As you can see from the above example, square now has a property called myCustomProperty!

But now you may think that this won't cause problems because you will only call the properties you know exist in the object you create; and well, that may be true but the real problem occurs when you use the for...in statement.

In JavaScript, there exists a construct that lets you iterate over all the properties of an object, the for...in statement:

Object.prototype.myCustomProperty = 200;

var box = { width: 200, height: 500 };

var props = "";
for (var p in box) {
    props += p + "\n";
}
alert(props);

In the above code I am iterating over every property of the box object and appending the name of that property to a string called props.

The output of the above alert is :



So you see, myCustomProperty now appears as part of the box object! And when you iterate over properties of an object, you will want the properties of only that object and not properties that are up the inheritance chain!

Fortunately enough, there is a workaround for this. This workaround involves using the hasOwnProperty function:

Object.prototype.myCustomProperty = 200;

var box = { width: 200, height: 500 };

var props = "";
for (var p in box) {
    if (box.hasOwnProperty(p)) {
        props += p + "\n";
    }
}
alert(props);

Now the alert displays:


This is because the hasOwnProperty returns true only if the property in the current iteration is directly linked to the object you are using in the loop; and thus, it returned false for the myCustomProperty property.

So make sure that when iterating over properties in JavaScript, you use the for...in construct! ...or else bad things may happen if someone (probably some JavaScript framework) extends the Object.prototype!