views:

81

answers:

2

Is it possible to subclass and inherit from javascript Arrays?

I'd like to have my own custom Array object that has all the features of an Array, but contains additional properties. I'd use myobj instanceof CustomArray to perform specific operations if the instance is my CustomArray.

After attempting to subclass and running into some problems, I found this Dean Edwards article that indicates doing this with Array objects doesn't work right. It turns out Internet Explorer doesn't handle it properly. But I'm finding other issues as well (only tested in Chrome so far).

Here's some sample code:

/** 
 *  Inherit the prototype methods from one constructor into another 
 *  Borrowed from Google Closure Library 
 */
function inherits(childCtor, parentCtor) {
    function tempCtor() {};
    tempCtor.prototype = parentCtor.prototype;
    childCtor.superClass_ = parentCtor.prototype;
    childCtor.prototype = new tempCtor();
    childCtor.prototype.constructor = childCtor;
},

// Custom class that extends Array class
function CustomArray() {
    Array.apply(this, arguments);
}
inherits(CustomArray,Array);

array = new Array(1,2,3);
custom = new CustomArray(1,2,3);

Entering the following in Chrome's console gives this output:

> custom
[]
> array
[1, 2, 3]
> custom.toString()
TypeError: Array.prototype.toString is not generic
> array.toString()
"1,2,3"
> custom.slice(1)
[]
> array.slice(1)
[2, 3]
> custom.push(1)
1
> custom.toString()
TypeError: Array.prototype.toString is not generic
> custom
[1]

Obviously, the objects don't behave the same. Should I give up on this approach, or is there some way to accomplish my goal of myobj instanceof CustomArray?

A: 

I've tried to do this sort of thing before; generally, it just doesn't happen. You can probably fake it, though, by applying Array.prototype methods internally. This CustomArray class, though only tested in Chrome, implements both the standard push and custom method last. (Somehow this methodology never actually occurred to me at the time xD)

function CustomArray() {
    this.push = function () {
        Array.prototype.push.apply(this, arguments);
    }
    this.last = function () {
        return this[this.length - 1];
    }
    this.push.apply(this, arguments); // implement "new CustomArray(1,2,3)"
}
a = new CustomArray(1,2,3);
alert(a.last()); // 3
a.push(4);
alert(a.last()); // 4

Any Array method you intended to pull into your custom implementation would have to be implemented manually, though you could probably just be clever and use loops, since what happens inside our custom push is pretty generic.

Matchu
@Matchu: Thanks, but this solution doesn't really create an object that performs like an array. You can add the methods to it, like you did with `push`, but it doesn't handle direct index manipulation. For instance, doing `a[5]=6` won't change the length like it would in a real Array. The article linked in @CMS answer goes over all the possible solutions and points out the flaws.
Tauren
@Tauren - aha, I knew there was something obvious I was missing :) Neat article - shame I hadn't found it before!
Matchu
+5  A: 

Juriy Zaytsev (@kangax) just today released a really good article on the subject.

He explores various alternatives like the Dean Edwards iframe borrowing technique, direct object extension, prototype extension and the usage of ECMAScript 5 accessor properties.

At the end there is no perfect implementation, each one has its own benefits and drawbacks.

Definitely a really good read:

CMS
@CMS: Great article and perfect timing! Thanks.
Tauren