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"));
Nifty. I’m a bit surprised that slice works like that.
That last bit doesn’t work for me unless I do
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.argumentsnot being an array can be annoying though.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.
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!
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!
But what doesn’t work is sending a resonse!
The half of my response above is missing!!
Pingback: [翻译]Javascript中类似数组的对象 - interUI
“Thus, if you do typeof [] you get ‘object’ back.”
This is actually the correct object type you should get back from an array. There are three objects in javascript that return “object” – object (go figure), array, and null. Not very useful, yes, i know.
Crockford wrote a helper method to alleviate that issue and return the true type of an object – http://javascript.crockford.com/remedial.html (typeOf() method)
Pingback: Javascript里的Array-Like对象
Pingback: Steg 2a – förstå call() och apply()