A Low-down, Dirty Goblin Of A Hack

This is a dirty trick, it’s so dirty that I feel bad and wrong for even mentioning it but JavaScript just makes me do these things. It’s not my fault….I’m not in control here. You know when you are trying to work with the arguments array but you realise that it’s not really an array so it doesn’t have all those good methods like slice(), concat() etc. Well, there’s a way around that but it’s not pretty.

You see, JavaScript methods aren’t in anyway tightly bound to their object like in many other languages. If you like a method that a another object has you can just steal it. In the case above, we like that slice() method that arrays have, let’s just steal it:

[].slice.call(arguments, 1);

Ha! We created an empty array just to steal it’s slice() method then use call() to point this at the arguments array! And you know what? It works rather nicely on quite a lot of objects and methods. Go on, have a play…

15 Comments (Closed)

Now I feel sick.

silsil at 07.11.06 / 11AM

When using Prototype.js you can simply $A(arguments).

MislavMislav at 07.11.06 / 11AM

I feel dirty just reading it…;-)

Luke RedpathLuke Redpath at 07.11.06 / 11AM

Actually it works on all array-like objects, say:

[].slice.call({0: ‘foo’, 1: ‘bar’, length: 2}, 1) // ‘bar’

I tested this in the JavaScript engine from Mozilla, what other engines support this?

Mark WubbenMark Wubben at 07.11.06 / 12PM

Awesome!

Bramus!Bramus! at 07.11.06 / 12PM

Mislav: Yeah, good point. I forgot to mention that actually. In most cases cloning a whole array just to use one method is a bit of an overkill though. That’s why I keep the goblin handy :)

DanDan at 07.11.06 / 12PM

Mark: It’s pretty stable in my experience. I’ve had a quick test and it seems to work in:

  • IE 6/7
  • Firefox 1.5 / 2
  • Opera 9
  • Safari 2.03 and Webkit

DanDan at 07.11.06 / 13PM

Instead of creating a new array object just to throw it away later, you can directly access Array.prototype:

Array.prototype.slice.call(arguments, 1);

More verbose, but it feels a little less dirty.

Simon WillisonSimon Willison at 07.11.06 / 13PM

Or….you could do a similar thing but in a more grimy way by just using the slice() method of any random array that’s in the scope….Wa ha ha!

It’s a shame that all the array methods don’t have a generic version like Array.forEach() in JS 1.6. Then we wouldn’t need to resort to all this evil stuff.

DanDan at 07.11.06 / 13PM

This is the logical conclusion to the preceding comments:
if (!Array.slice) { // mozilla already supports this
  Array.slice = function(object) {
    var slice = Array.prototype.slice;
    return slice.apply(object, slice.call(arguments, 1));
  };
}
Now you can do this:
Array.slice(arguments, 1);

Dean EdwardsDean Edwards at 07.11.06 / 15PM

Thattud do it!

DanDan at 07.11.06 / 16PM

Dean stole my comment.

The natural extension of this idea is to provide Array generics for all Enumerable methods and Array builtins. It’d come in handy for arguments and node lists.

Andrew DupontAndrew Dupont at 07.11.06 / 19PM

I suppose you can use a swiss method to take care of it as well.

Function.prototype.swiss = function(house) {
    for ( var i = 1; i < arguments.length; ++i ) {
        var thief = arguments[i];
        this[thief] = house[thief];
    }
    return this;
};

Then it can easily be applied in this case

var fn = function() { };
fn.swiss(Array.prototype, 'map');

You’ve now stolen ‘map’ from Array: fn.map(f);

Dustin DiazDustin Diaz at 07.11.06 / 22PM

Ah, this is so evil, I love it.

It opens up a whole world of using internal JS functions for totally new purposes. And I bet we could figure out some amazing hacks. What else is there to abuse?

Yes, I have a dirty mind.

Jesse SkinnerJesse Skinner at 08.11.06 / 05AM

That’s sweet.

Dr NicDr Nic at 17.11.06 / 10AM

About This Article