arguments considered harmful

Inside every JavaScript function an arguments object is available containing all the parameters passed to the function.

function aha(a, b) {
  console.log(arguments[0] === a); // true
  console.log(arguments[1] === b); // true
}
 
aha(1, 2);

However, it's not a good idea to use arguments for the reasons of :

  • performance
  • security

The arguments object is not automatically created every time the function is called, the JavaScript engine will only create it on-demand, if it's used. And that creation is not free in terms of performance. The difference between using arguments vs. not using it could be anywhere between 1.5 times to 4 times slower, depending on the browser (more info and bench)

As for the security, there is the POLA (Principle of Least Authority) which is violated when one function A passes arguments to another B. Then a number of bad things can happen including:

  • B calls A through arguments.callee - something A never intended when calling B in the first place
  • B overwrites some arguments and causes A to misbehave

While in these scenarios B looks a little malicious, it can actually cause trouble unvoluntarilly. Consider this example that illustrates the second case (B changing values behind A's unsuspecting back)

function A(obj, ar) {
 
  console.log(obj); // {p: 1}
  console.log(ar);  // [1, 2, 3]
 
  B(arguments);
 
  // oops!
  console.log(obj); // {p: 2}
  console.log(ar);  // [1, 2]
}
 
function B(args) {
 
  // convenient innocent-looking local vars
  var o=args[0],
      a=args[1];
 
  // do something with the local variables
  o.p = 2;
  a.pop();
 
  // now the original arguments is 
  // messed up because objects/arrays
  // are passed by reference
}
 
A({p: 1}, [1, 2, 3]);

ECMAScript 5

In ECMAScript's "strict mode", using arguments.callee will throw a syntax error.

Recursive anonymous function

Probably the biggest argument for keeping arguments and arguments.callee is so that recursive anonymous functions can be created, because by using the callee property a function can call itself without knowing its own name. Now, this is not such a common scenario, but even if so, you can wrap a named function inside of an anonymous function and voila! - call that named function recursively without leaking a variable to the global scope.

Tags: , ,

8 Responses to “arguments considered harmful”

  1. Jordan Boesch Says:

    Interesting! I always knew it was a bit of a performance hit to bring ‘arguments’ in to the local scope but I had no idea it passed arguments by reference. I noticed if you change

    B(arguments);
    to
    B([obj, ar]);

    it has the same effect, so I guess this isn’t specifically a problem with the ‘arguments’ object… thoughts?

  2. Dmitry A. Soshnikov Says:

    Hi, thanks for the article.

    The arguments object is not automatically created every time the function is called, the JavaScript engine will only create it on-demand, if it’s used

    Which exactly “JavaScript engine” is meant? Where did you get this exact info? Although, it can be true in some implementations (yep, it’s the good optimization as all needed info about the context is available on parsing the code, so there’s no need to create arguments object if it was not found on parsing), but as you know ECMA-262-3 statements, that arguments object is created each time on entering the execution context.

    Consider this example that illustrates the second case (B changing values behind A’s unsuspecting back)

    Nothing is wrong, nothing is odd. It’s the same if you simply pass an object (or an array to A and then will pass this argument to B). Moreover, arguments name can be used (although, it will be an error in “strict mode” of ES5):

    function foo(arguments) {
    console.dir(arguments); // {a: 10, b: 20}
    bar(arguments);
    // oops? why “oops”? that’s the common
    // ECMAScript behavior
    console.dir(arguments); // {a: 100, b: 20}
    }

    function bar(args) {
    args.a = 100;
    }

    foo({a: 10, b: 20});

    Dmitry.

  3. stoyan Says:

    this is valid point you guys raise, it’s not arguments that is to blame, it’s the fact that objects are passed by reference. so the same will happen with any object. However, when A is passing arguments to B, A probably doesn’t expect them to change, just because of the nature of the task – passing arguments. My example was just showing how easy it is for B to change them even unintentionally. Of course `A` can be defensive and pass a deep copy of arguments so it doesn’t get overwritten by mistake.

    Dmitry, I was referring to these tests:
    http://webreflection.blogspot.com/2010/02/arguments-callee-call-and-apply.html
    Looks like all JS engines avoid creating arguments, unless necessary. V8 is the only engine where performance doesn’t suffer but only if there’s no arguments passed.

  4. stoyan Says:

    @Dmitry, I just looked around javascript.ru – it’s an incredible resource, good job!

  5. Mathias Bynens Says:

    Here’s a jsPerf test case showing the performance implications of using the arguments object: http://jsperf.com/arguments

  6. Poncjan Says:

    How much of an exciting short article, continue posting mate

  7. pipe Says:

    http://misjazdrowia.pl/ misja zdrowia

  8. Parker Says:

    Instead of scaring people away from using the *incredibly* useful arguments variable, why don’t you instead teach others how to pattern around these issues ;) ?

Leave a Reply