Say you’re writing a function which takes a function name as an argument and passes the rest of its arguments to the named function. Your unit test for such a function may look like this:

assertEquals ( 'add' , 5 , calculate ( 'add' , 3 , 2 ) ) ; assertEquals ( 'mul' , 6 , calculate ( 'mul' , 3 , 2 ) ) ;

We would like to splice off the first element and pass the rest to another function but since arguments isn’t a real Array it doesn’t support the splice method. Never mind, that’s easily treated by calling splice with arguments as this .

function calculate ( operation ) { // Remove `operation' from arguments leaving only `a' and `b' Array . prototype . splice . call ( arguments , 0 , 1 ) ; var funcs = { add : function ( a , b ) { return a + b ; } , mul : function ( a , b ) { return a * b ; } } return funcs [ operation ] . apply ( this , arguments ) ; }

So why are we getting an Uncaught TypeError: Cannot call method ‘apply’ of undefined? Looking at things in Chrome’s debugger everything looks OK. operation is “add” and funcs[operation] is a function.



It dawned on me that the debugger is lying (Firebug and IE’s debugger don’t have this problem), splice removed the first parameter from arguments and now the value of operation is 3 . How come 3? Well operation was the first parameter and the first element in arguments is now 3, it’s obvious really(?).

Now I picked up JavaScript on my own (as I assume many do) so I’m no expert and this took me by surprise (it’s probably not trivial even for real JavaScript developers if Chrome’s debugger has this defect).

After meditating about the facts it makes sense that formal parameters and the elements in the arguments object are aliases so that if you do arguments[2]++ the formal parameter ‘b’ changes too but this behaviour means that that these references are more like Perl’s much vilified symbolic references than most languages’ references. This means that using a formal parameter in JavaScript is semantically equivalent to indexing into the arguments object, writing operation in (this example) is just syntactic sugar for arguments[0] . If you want to remove an argument from the arguments object and still used the named parameter you must first cache the value in a separate variable.

Here’s the fixed version of calculate:

function calculate ( operation ) { var op = operation ; // Save value of `operation' // Remove `operation' from arguments leaving only `a' and `b' Array . prototype . splice . call ( arguments , 0 , 1 ) ; var funcs = { add : function ( a , b ) { return a + b ; } , mul : function ( a , b ) { return a * b ; } } return funcs [ op ] . apply ( this , arguments ) ; }

Edit: as strager said in the reddit thread:

Takeaway:

Do not modify the arguments object.

Ever.

Now that I’m older and slightly less ignorant I think this would be a better solution:

function calculate ( operation ) { var funcs = { add : function ( a , b ) { return a + b ; } , mul : function ( a , b ) { return a * b ; } } return funcs [ operation ] . apply ( this , Array . prototype . slice . call ( arguments , 1 ) ) ; }

Edit January 2012: The Chrome defect has been fixed (verified on Chrome 16).