Why the ‘arguments’ object is bad, Exhibit #3,745

Having just written a post with an example of how to use the arguments object, you might question my choice of title for this blog post. In my not-so-humble opinion, the 'arguments' object in ECMAScript is only useful for one thing:

The 'arguments' object should only be used by infrastructure functions that simply perform book-keeping on behalf of other functions. Any function that actually operates on its arguments in a meaningful way should never access the 'arguments' object.

The reason for this is plain and simple: code should be easy to read, easy to debug, and easy to maintain. Saving yourself a small amount of effort while writing code is a very bad trade-off if it negatively impacts these other three important tasks. Using the 'arguments' object in any normal function might save you a few keystrokes in the short-term, but will come back to bite you later on.

The most obvious problem with the arguments object is this: You have some code you wrote a few [days|weeks|years] ago and now you have to go and update it. You see some function calls like this in source code:

 

SomeBadFunction(42);
// ...

SomeBadFunction(i, current);
// ...

SomeBadFunction(next, data, OtherFunction(), moreData);
// ...

 

You think the problem might be related to one of the parameters being passed to SomeBadFunction, so you decide you need to check it out:

 

function SomeBadFunction()
{
// 200 lines of code
}

 

Oh dear. The definition of SomeBadFunction gives us absolutely no idea how many parameters it expects, what order they should be in, nor what values they might represent. Maybe there's a comment at the top of the function that explains what you should pass it, but we all know that comments get out of date very quickly and the code is the only real way to know what is going on. So now you have to trawl through several hundred lines of code, trying to reverse-engineer what kind of parameters the function expects. This could take a long time, and you might get it wrong.

Let's imagine that the real intent of SomeBadFunction is to update an internal table. The first parameter is the index of the item in the table to update, and the second parameter is the value to place in the table. The function supports rudimentary format strings (like printf() in C) and so if the value contains a format-specifier like "%d" or "%s" then the remaining parameters are used to fill in the blanks. Finally, if the value is omitted then the table entry is removed.

In this case, I would define the function something like this:

 

function SomeBadFunction(index, value, formatArgs)
{
// probably fewer than 200 lines of code
}

 

Now it is clear that SomeBadFunction expects three arguments, the first should probably be a number, the second should be some generic value, and the third should be an array of values (by using the naming convention ...Args; you could also call it formatArgArray or paramarray or varargs or something else if you feel that is clearer).

ECMAScript already allows for omitting function arguments, and code is typically written to have some "obvious" behaviour if parameters end up being undefined, so simply dropping value or formatArgs from a function call is quite OK. Now the only thing you need to change when you call the function is that if – if – you want to use the special format-string ability of SomeBadFunction, you simply encase the trailing arguments in square brackets to put them in an Array:

 

SomeBadFunction(next, data, [OtherFunction(), moreData]);

 

Anyway, I promised you an exhibit of why 'arguments' is bad – a bona-fide bug in ECMAScript, not just the rambling opinions of some Microsoft fuddy-duddy. Take this code:

 

function foo(x, y)
{
print("x: " + x + ", y: " + y);
Array.prototype.shift.call(arguments);
print("x: " + x + ", y: " + y);
}

foo(42, 123);

 

What do you expectit to display? Don't ruin the fun by running it and posting the actual answer – just post what you think it is.

I say this is a bug in ECMAScript – rather than a bug in JScript – because section 10.1.8 of the ECMA 262 spec doesn't completely define how the arguments object interacts with the parameters of the function, and so you get some strange results. Note that both IE and Firefox give you the same result (replace 'print' with 'alert') but I doubt both companies independently came up with the same unexcpected result. I don't know if this is a case of IE being bug-compatible with the original Netscape Navigator code, or Firefox being bug-compatible with IE code, or a bit of both. I wasn't around when this was happening :-)