Wednesday, May 21st, 2008

Stack: A native Array wrapper that works

Category: JavaScript, Library, Performance

<p>Andrea Giammarchi has created a native array wrapper that works across browsers. He is excited:

I do not know how many times, during this years, JavaScript Ninjas have tried to subclass the native Array to create libraries over its powerful methods without losing performance. I have finally discovered the way to remove the locked length from Internet Explorer 8, and to solve problems with every other browser.

He ended up with the Stack class:

javascript
< view plain text >
  1. /**
  2.  * Choose a name for subclassed Array
  3.  */
  4. Stack = (function(){ // (C) Andrea Giammarchi - Mit Style License
  5.  
  6.     /**
  7.      * Your personal Array constructor
  8.      */
  9.     function Stack(length){
  10.         if(arguments.length === 1 && typeof length === "number")
  11.             this.length = -1 < length && length === length << 1 >> 1 ? length : this.push(length);
  12.         else if(arguments.length)
  13.             this.push.apply(this, arguments);
  14.     };
  15.  
  16.     // Solution 1:
  17.     //      Declaration of generic function
  18.     //      with an array as prototype
  19.     function Array(){};
  20.     Array.prototype = [];
  21.  
  22.     // Solution 2:
  23.     //      use the prototype chain to inherit
  24.     //      Array constructor and its native prototype
  25.     Stack.prototype = new Array;
  26.  
  27.     // Solution 3:
  28.     //      overwrite inherited length with zero value
  29.     Stack.prototype.length = 0;
  30.  
  31.     // Solution 4:
  32.     //      redeclare toString method in this way
  33.     //      to let JScript core feel better
  34.     Stack.prototype.toString = function(){
  35.         return  this.slice(0).toString();
  36.     };
  37.  
  38.     /**
  39.      * Return and assign subclassed Array
  40.      */
  41.     Stack.prototype.constructor = Stack;
  42.     return  Stack;
  43.  
  44. })();

Related Content:

Posted by Dion Almaer at 10:00 am
10 Comments

++++-
4.2 rating from 21 votes

10 Comments »

Comments feed TrackBack URI

You’re joking… If this is true this could mean a huge change for some libraries. I’ll have a closer look as soon as I can.

Comment by Anonymous — May 21, 2008

It’s a clever solution, although it’s a shame about concat… maybe with a little bit more work we can get around that too :)

Comment by Anonymous — May 21, 2008

This is really good news and I amazed that it really works. I would love to see how it compares in construction speed. Since many frameworks out there tends to create lots and lots of arrays so the speed of creation might be an interesting factor too.

Comment by Spocke — May 21, 2008

As far a I know the only problem was IE. All other browsers are free to use the normal prototype chain to do the actual inheritance. This is the best solution then, performance and clean code wise.

In IE there exist the idea to borrow the Array from another document. This could also be done without iframes using the “htmlfile” ActiveX object. This is also the solution qooxdoo does in 0.8-alpha1. You can see the implementation details here: List.js (from qooxdoo)

Comment by wpbasti — May 21, 2008

Another problem left to solve is that after subclassing an Array one would like the return types of Array.prototype methods to be of the subclass type instead of its superclass type Array.

Comment by Anonymous — May 21, 2008

Andrea, I know that ActiveX can be disabled. Your solution is definitely interesting and appreciated that every few months people try to find better ways to work-around the issue.

Can you tell we which parts exactly are needed to get the wanted behavior in IE. I still think that the normal prototype chain is OK in other browsers. So which of the “Solution” blocks are really needed, which are optional? Must the length really be fixed.

Comment by wpbasti — May 21, 2008

Subclassed Arrays could use native method to work internally (each, forEach, some, every, reverse, join, splice, etc …) but for those methods that returns a derived copy, you should wrap them and convert copy into your desired instance.
This is what I have done with ArrayObject, using a prototype called “to” which aim is to convert every kind of array like object into a specified array like compatible constructor.
Unfortunately, that prototype should be declared as Array.prototype, exactly what we wouldn’t do, but I will write something about this problem and possible solutions ( specially, because I have to rewrite the ArrayObject too :) )

Comment by Andrea Giammarchi — May 21, 2008

wbpasti, for every browser but not IE8, remove Solution 1 and 3.
If you change the toString prototype, for example, it will not work in many case and with some method (at least my FireFox).

Since the prototype chain points directly to an Array, I think that there is no valid reason to split the code for those browsers that do not soffer blocked length, and IE8 (I mean, performances should not increase that much)

Finally, the most clever browser with these cases, is Opera, that is nearly compatible with concat, and allows us to use directly Stacks as apply arguments, and without conversion. Welll done Opera!

Comment by Andrea Giammarchi — May 21, 2008

KevinWeibell, we all know Dean solution. This one does not require iframes, popups, ActiveX, whatever, it is just JavaScript, without tricks that could make things complicated (i.e. SSH for iframes, or not enough DOM to add iframes, or ActiveX disabled, or popup blocked by browser, or by third parts components).

For those interested in an advanced example that does not soffer concat or splice convertion to Array problems and has some nice feature, I have updated my ArrayObject, and I am going to write a post about them.

Comment by Andrea Giammarchi — May 21, 2008

This is a much better way to do this.


var Stack = (function() {

function Stack(){
this.push.apply(this, arguments);
}

function emptyFunction(){}
emptyFunction.prototype = Array.prototype;
Stack.prototype = new emptyFunction;

return Stack

})();

Comment by deadlyicon — February 6, 2010

Leave a comment

You must be logged in to post a comment.