It make look like an Array, hell, it even has a length attribute; however, it may not actually be an Array. JavaScript is sometimes a quirky language to say the least and the notion of what is an Array is no exception.

So what are these array-like objects that I speak of? Well there are a few of them, one of which is arguments. arguments is a special variable that is available inside the body of every function. It is in fact, the list of arguments that were passed in.

arguments

If you inspect the arguments variable in a tool such as Firebug you will notice that it prints out like an array. It has elements in sequential order and it even has a length property.

var testFunction = function() {
  console.log(arguments);
  console.log(arguments.length);
};

So what am I griping about? Try doing arguments.shift(). Uh-oh, looks like arguments.shift isn’t a function; but that’s a function of an Array. Another bit of fun you can do is console.log(arguments.constructor). That will print to your console “Object()” while if you did [].constructor, that will print “Array()”. Having fun yet?

This also isn’t just limited to arguments. It seems that a lot of the DOM(document object model) collections are returned as these objects rather than arrays: document.getElementsByTagName(), document.images, document.childNodes. In some cases, these array-not-arrays would be better suited to become arrays.

Making the Array-like Objects become Arrays

Well, that heading is a misnomer. If we want those array-like objects to behave like arrays, we are going to need a new array.

var testFunction = function() {
  // Create a new array from the contents of arguments
  var args = Array.prototype.slice.call(arguments);

  var a = args.shift();
  console.log("The first argument is: %s", a);

  // send the remaining arguments to some other function
  someOtherFunction(args);
};

Clearly, the magic is all in Array.prototype.slice.call(arguments). So let me break it down per piece.

Array
this is the name of the base object that we want
prototype
this can be thought of as the namespace for the instance methods of an array
slice
this extracts a section of an array and returns a new array, and without a beginning and ending index, it simply returns a copy of the array
call
this is a very useful function, it allows you to call a function from one object and use it in the context of another

Tada! As a side note, if you are used to using the prototype framework then you can convert these array-like objects to arrays using $A(), even though Prototype does it’s array conversion differently.

Speaking of $A. Let’s say you don’t like typing that long string code above and *you’re not using Prototype*. Then you can create a shortcut just like the Prototype folks:

var $A = function(obj) {
  return Array.prototype.slice.call(obj);
};

// Example usage:
$A(document.getElementsByTagName("li"));
Published in JavaScript

5 Responses to “Array-like Objects in JavaScript”

  1. June 29th, 2007 at 8:24 am #Tore Darell

    Nifty. I’m a bit surprised that slice works like that.

    That last bit doesn’t work for me unless I do

    var $A = function(){ return Array.prototype.slice.call(arguments); }

    And even the “real” array returned by this won’t actually be an array in the traditional sense.. Arrays in JS are basically just hash maps (objects) that use integers as keys and behave like arrays.. Thus, if you do typeof [] you get ‘object’ back. Quirky, indeed. arguments not being an array can be annoying though.

  2. June 29th, 2007 at 9:27 am #K. Adam Christensen

    D’oh. Yeah, @var $A = Array.prototype.slice.call@ won’t work at all. What was I thinking!?!? Good catch!

    Array isn’t a primitive data type as you noticed that @typeof []@ will return @object@; however, if you do @[].constructor@, you will see that it is an Array.

  3. September 6th, 2007 at 11:08 pm #Pops

    Dude! You are a freaking LIFE SAVER! I’m a veteran C/C++ guy dealing with pointers all the freaking time, and I was trying desperating to fiqure out how to pass the poitner to the arguments[1] after my function analyzed the optional first object parameter.

    I spent hours tryiing to do this, trying to shift directly, shifting a copy, trying to pass a c-like &arguments[1]… NOPE!!

    Your prototype solution hurts because I NEED TO KNOW JAVASCRIPT BETTER!

    My final solution is

    function log(prefix, argv)
    {
    if (typeof(prefix) != “string”) {
    var args = Array.prototype.slice.call(arguments); // EUREKA!
    logArgs(args,prefix);
    } else {
    logArgs(arguments,”");
    }
    }

    Thanks a million. I had to write to tell ya YOU helped mankind!

  4. December 11th, 2008 at 8:27 am #mister

    You can also find out that $A returns an Array by checking with:

    var arr = $A(1,2,3);
    if ( !(arr instanceof Array) )
    document.write(“!!!shit!!!”);

    There is no !!!shit!!! it works!

  5. December 11th, 2008 at 8:31 am #mister

    But what doesn’t work is sending a resonse!
    The half of my response above is missing!!

Leave a Reply