debuggable

 
Contact Us
 

Turning JavaScript's arguments object into an array

Posted on 1/10/09 by Felix Geisendörfer

JavaScript is awesome when you stick to the good parts. But sometimes you have to touch it's bad parts. Consider this:

function hello() {
    arguments.unshift('hello');
    alert(arguments.join(' '));
}

hello('pretty', 'world');

"TypeError: arguments. unshift is not a function"! How dare you JavaScript?

JavaScript might trick you into thinking the arguments variable is an array because you can access it like one (arguments[0], arguments[1], ...), but it's lying. The truth is that the arguments variable really is an object.

Lucky for us, the good parts of JavaScript come to rescue, namely prototypical inheritance:

function hello() {
    var args = Array.prototype.slice.call(arguments);
    args.unshift('hello');
    alert(args.join(' '));
}

hello('pretty', 'world');

This creates a variable called args that holds a true array version of the arguments variable. This works by hjacking the Array.splice function to make it work on the arguments variable.

Yes, this is the kind of code you might want to document for the poor kids who will have to debug grandpa's "AJAX museum" one day.

Got a better solution? Let me know!

-- Felix Geisendörfer aka the_undefined

 
&nsbp;

You can skip to the end and add a comment.

Christoph Tavan said on Oct 01, 2009:

Sorry for being OT but I love that photo :)

Felix Geisendörfer said on Oct 01, 2009:

Christoph Tavan: I just made it, but I have to admit I stole the idea from someone else but I forgot the url - otherwise I would have given credit. It's soo true so.

Karl E Swedberg said on Oct 02, 2009:

Hey Felix,

Nice post. That little trick has come in handy quite a bit. It's also useful when you're working with a collection of DOM nodes.

Kind of crazy, but if you want to save some bytes, you can use [] instead of Array.prototype:
var args = [].slice.call(arguments);

Malte Ubl said on Oct 02, 2009:

Hi,

actually that statement is a generic tool to turn "Enumerable" JS objects (objcts with a length attribute) into real arrays. Cant be used on DOM lists, etc. jQuery uses it a lot for example.

Cheers
Malte

Felix Geisendörfer said on Oct 02, 2009:

Karl E Swedberg: Perl roots? : )

Malte Ubl: Yes, you've put it perfectly!

Karl E Swedberg said on Oct 02, 2009:

Oops! My mistake. Scratch that part. :)

At least the shortcut part ([].slice.call()) is correct. Or is it? Now you've got me doubting myself. ;)

Felix: no Perl roots. Just general hackery.

Felix Geisendörfer said on Oct 02, 2009:

[].slice.call() works, but is slightly different. [] will create a new empty array who's slice function is then being hijacked. However, the empty array instance is "wasted" as slice() itself will return a new array. So I would expect the [] version to be slower. But my tests have shown it to be pretty much identical in speed (tested v8 and spidermonkey). Try it out for yourself:

var arguments = {
  0: 'foo',

  1: 'bar',

  length: 2

};



var start = (+new Date);

for (var i = 0; i < 1000000; i++) {

   var args = Array.prototype.slice.call(arguments);

}

var prototypeDuration = (+new Date) - start;





var start = (+new Date);

for (var i = 0; i < 1000000; i++) {

   var args = [].slice.call(arguments);

}

var instanceDuration = (+new Date) - start;



console.log(prototypeDuration);

console.log(instanceDuration)

console.log((instanceDuration / prototypeDuration * 100).toFixed(2) - 100);
Bill  said on Nov 28, 2009:

I am having great difficulty understanding Array.prototype.slice.call(arguments) -- why does this work? I thought you had to pass a context object as the first parameter to the call method. Does slice implement its own call method?

This post is too old. We do not allow comments here anymore in order to fight spam. If you have real feedback or questions for the post, please contact us.