Responsible JavaScript: Prototype Modification

JavaScript is a fully object oriented programming language that supports multiple inheritance models. At the heart of it all, JavaScript uses prototypal inheritance. Even the default types (String, Boolean, Number, …) are objects themselves. When you “extend” something, you don’t extend a class — instead, objects inherit directly from other objects.

Some libraries (Prototype, for example) make heavy use of modifying native object prototypes. This has the instant effect of adding significant functionality to all instances of the native objects.

// Create a new array instance and populate it with some data.
var a = ['foo', 'bar', 'baz'];

// Modify Array's prototype to include new functionality
Array.prototype.each = function (callback) {
    for (var i = 0, l = this.length; i < l; i++) {
        callback(this[i], i);
    }
};

// Even though 'a' was defined prior to the pototype modification,
// the effects are visible. This is the power of prototypal inheritance.
a.each(function (value, index) {
    console.log(index + ' === ' + value);
});

While the temptation is great, responsible developers should think twice about modifying native object prototypes. One may argue that “additive” methods pose no harm. Still, by adding behavior, your casting your dependencies on the language itself thereby making documentation and debugging challenging.

What’s even worse is when developers get clever and modify Object.prototype. This of course has the effect of modifying all objects of all types. The most obvious repercussion of this is that it breaks the for..in pattern which is commonplace in most JavaScript applications

Object.prototype.foo = function () {
    console.log('foo');
};

var o = {'hello':'world'};

for (var i in o) {
    console.log(i + ' === ' + o[i]);
}

At this point, the pros in the crowd will yell “yeah, you should be using hasOwnProperty.” And rightfully so, as that is the only way to insure that we’re dealing with members local to our object in question. But what if we want to get an aggregate list of properties local and inherited? If you’ve futzed with Object.prototype, you’ll have to use recursion … I hope you’re happy now.

Don’t Modify. Extend.

Instead of modifying the native object, simple create a new data type by extending it as you would any other object.

var MyArray = function () {};
MyArray.prototype = new Array;

var ma = new MyArray;
ma.push('foo');
ma.push('bar');
ma.push('baz');

console.log(ma.join(','));

This technique offers the same end result without compromising the integrity of the application. It’s more portable and allows for easy documentation. This is also the natural action if you are familiar with classical inheritance. While applying classical development methodologies to languages such as JavaScript is generally a bad idea, in this particular case it’s the responsible thing to do.

This entry was posted in JavaScript. Bookmark the permalink.

5 Responses to Responsible JavaScript: Prototype Modification

  1. mikeg says:

    For those who are interested, there’s a great post on Ajaxian about this very topic. The discussion that follows is quite interesting too.

    http://ajaxian.com/archives/de-fusing-javascript-natives-with-the-fusebox?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+ajaxian+%28Ajaxian+Blog%29

  2. Fire Crow says:

    The extensions idea, is a really good point, it’s not entirely obvious what the harm is in “adding” to an object, but when several libraries have different ideas i gets messy.

    Making a new object is very important for portability.

    With a custom object such as MyArray, multiple libraries can start defining thier own objects LibraryOneArray. etc.

    cool post !
    – fire

  3. Ben Nadel says:

    Good stuff my man; as far as extending arrays, the one thing I can’t figure out for the life of me is how to get bracket-notation to play nice with the new class. It looks like everyone who takes this approach ends up deferring to push/pop/shift/unshift to mutate the array. Is there now way to use the a[ 1 ] notation with the override?

    • mikeg says:

      @Ben, Unfortunately I’m not aware of any way of doing that. Though, in the cases where I find myself extending natives like this, I don’t mind the extra verbosity as I think it leads to clearer code in the long run.

      Whenever I find myself extending a native object, I usually need to adapt it for a special purpose. For example a Stack or Queue. In hindsight, my synchronous executor would have done nicely as an extension to Array.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>