Javascript and the extended Array prototype

on April 13th, 2009 at 5:27pm , 6 responses

I do quite a bit of javascript stuff lately, and I wanted to enjoy the easier syntax of array methods such as forEach, find etc.

As the current project is not using prototype.js, but rather a different js stack (jquery, various jquery plugins, EJS, and a bit more) I did not have the extended Array stuff that comes with prototype.js

 

But before I ran off to add the needed methods to Array’s prototype, I had an annoying voice in the back of my head, whispering “extending Array’s prototype is evil, extending Array’s prototype is evil”, so I looked at alternatives.

 

Alternative 1 – subclassing Array.

I went ahead to implement a MyArray (or Array2) type of solution.

using one method of JS subclassing I thought of

var MyArray = function() { }; MyArray.prototype = new Array; MyArray.prototype.forEach = function(action) { for (var i = 0, l=this.length; i < l, ++i) action(this[i]); }; ...

the problem with that approach is that IE does not like Array subclassing, thus the .length property becomes unreliable, rendering the whole idea of subclassing Array useless.

 

Alternative 2 – using a different object alltogether

It would work, however things like

if (anArrayInstance instanceof Array)

will naturally break.

 

Alternative 3 – extend any ‘interesting’ instances

function extendArray(arr) {
    if (arr.__wasExtended) return;
    arr.forEach = function(action) {
      for (var i = 0, l=this.length; i < l, ++i)
        action(this[i]);
    };
    arr.__wasExtended = true;
}

which is wrong as any instance will get a copy of all the functions, so too much memory will be used for non-core functionality

 

Alternative 4 – use the separated scoped Array trick

just read http://dean.edwards.name/weblog/2006/11/hooray/

the idea is to use an Array object from a separate iframe, thus enjoy the Array (instanceof), but not interfere with existing Array object on the main window

 

On top of all that. all three alternatives are problematic, as a regular

var a = [];

will not be extended. which is not such a big problem if you're disciplined enough, but it's terribly annoying to need to extend every array you want. think about JSON data you get from a service. you'd first have to iterate over the object graph and extend all of the arrays. yuck.

 

 

Now, do you remember that annoying voice from the back of my head? I decided to stand up to him !

Why actually not extend the Array prototype and be done with it?

It will solve the “instanceof” problem, it will solve the need to apply the functions manually on all arrays (as any [] will natively have the new functions), and it wouldn’t cost much memory as it will only be added to the single prototype of all array instances.

 

The usual reason for not wanting to do so, is that it would break the associative array ‘feature’ of javascript, and you won’t be able to

for (var i in myArray)

anymore.

 

You know what? that reason is a total bullshit.

Why? cuz there’s not such thing as an associative array in javascript !

If anything, the Object object is similar enough. However the Array object should be used with 0-based integer index, just like any native java/c#/c/whatever array.

 

Removing that ‘problem’ from the equation, and we can resort back to stuff like

Array.prototype.numSort = function() {
    this.sort(function(a, b) { return a - b; });
    return this;
};
Array.prototype.forEach = function(action, index) {
    for (var i = 0, l = this.length; i < l; ++i)
        action(this[i], index);
};
Array.prototype.find = function(func) {
	for (var i = 0, l = this.length; i < l; ++i) {
		var item = this[i];
		if (func(item))
			return item;
	}
	return null;
};
Array.prototype.where = function(func) {
	var found = [];
	for (var i = 0, l = this.length; i < l; ++i) {
		var item = this[i];
		if (func(item))
			found.push(item);
	}
	return found;
};

 

You just have to *love* dynamic languages :)

Mike Mike on April 13th, 2009 at 3:02pm
Hey KenSince you are using jquery why are you not using the jquery iterator for forEach and then use the .map call for your 'find' method?
Ken&#32;Egozi Ken Egozi on April 14th, 2009 at 5:51am
@Mike - not sure about .map, however .grep could have been useful here.But it would create a hard dependency on jquery all over the presentation logic (and in the said application there is quite a bit of that on the JS side). It's not that I'm an abstractions maniac, but I try to keep the jQuery usage for DOM manipulations, and abstract general object manipulations out. We even abstract ajax calls (although using $.ajax behind the scenes) but that's also because we use generated method proxies for mapping requests to the server.and by 'generated' I mean hand-rolled, as I didn't write that generator yet :)
avi avi on June 6th, 2009 at 3:08am
nice post, liked the buildup :)The 'problem' with extending the prototype of the Array is a programmer one.if all programmers in the team know they shouldn't use "for (var i in myArray)", then all is OK, but...(we had this problem using AjaxPro, the guy who wrote it extended the Array which gave us a lesson on iterating arrays :) )
Ken&#32;Egozi Ken Egozi on June 9th, 2009 at 6:26pm
@Avi: that's partly true. Even if all people on the team where aware of that, you could always have a problem if you try to use a 3rd party js that was built without this awareness. Surely you could usually find another version of the tool that can work for you, but it does render screening 3rd party integration more complex.
Martin&#32;Withaar Martin Withaar on February 9th, 2010 at 12:19am
Dynamic language paradigms make moral authorities out of programmers :)
Jay Jay on June 11th, 2012 at 4:17pm

So I have a issue with my app related to all these posts, and seeking solution to it. I have to extend the prototype as my code expect lot of Array methods which are not there in IE7-8. I added framework which extends Array prototype which as explained in this article spoils Associative array feature and it seems there are other part of code (3rd party - graph controls) fails as they are highly dependent on this associative feature of array. Please help!







Comment preview:

Follow

Statistics

Posts count:
447
Comments:
950