We're hiring!

We're actively seeking developers and designers for our Ann Arbor & Detroit locations.

JavaScript: Don’t Reassign Your Function Arguments

UPDATE: The point of this post is to raise awareness that reassigning the value of an argument variable mutates the arguments object. The code example is contrived and exists solely to help illustrate that behavior.

Did you know that a JavaScript function’s named parameter variables are synonyms for the corresponding elements in that function’s Arguments object?

I ran into this while experimenting with a function that was written to take either two or three arguments, providing a default for the first argument if only two are passed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var makePerson = function(favoriteColor, name, age) {
  if (arguments.length < 3) {
    favoriteColor = "green";
    name = arguments[0];
    age = arguments[1];
  }

  return {
    name: name,
    age: age,
    favoriteColor: favoriteColor
  };
};

var person = makePerson("Joe", 18);
console.log(JSON.stringify(person));
// => {"name":"green","age":"green","favoriteColor":"green"}

Strangely, all of the values in the result object are set to "green". I was expecting to see


  // => {"name":"Joe","age":18,"favoriteColor":"green"}

But when I set favoriteColor to "green" I was also changing arguments[0] to be "green". The situation only got worse when I set name = arguments[0] effectively changing arguments[1] to be "green" as well.

I had not realized that named arguments are synonyms for the elements of the Arguments object. I found a good explanation on Rx4AJAX:

The numbered properties of the Arguments Object are synonymous with the local variables that hold the named function parameters. They both reference the same address in the stack. If the function body has code that changes the value of a parameter either via a name reference or the arguments[] array reference, both referenced values will reflect the same value.

Regardless of the language, it is generally not a good idea to reassign the parameter variables inside a function. In JavaScript it turns out to be a really bad idea.

Additional information:

  • Check out this jsFiddle to experiment with the code snippet above.
  • A comment on this StackOverflow question describes this “magical behavior” of JavaScript.
  • JavaScript Garden describes this behavior in its section on the arguments object.
Patrick Bacon (46 Posts)

I’m a software developer at Atomic Object (since 2005) in Grand Rapids, MI.

This entry was posted in Web Apps and tagged . Bookmark the permalink. Both comments and trackbacks are currently closed.

16 Comments

  1. Posted April 10, 2011 at 10:49 pm

    So the arguments object isn’t a “real” array, so you run into problems when you treat it as such.

    Here’s a working example where you turn the arguments object into an array with Array.prototype.slice()

    http://jsfiddle.net/wookiehangover/yZPj8/4/

    This is a pretty common beginner’s mistake and is covered in most advanced books, such as Javascript Patterns or High Performance Javascript.

    Here’s a good resource about how the arguments object works: https://developer.mozilla.org/en/JavaScript/Reference/functions_and_function_scope/arguments

  2. Alexander Trefz
    Posted April 10, 2011 at 10:49 pm

    If you slice the Arguments, the get aan array, which is not “live”. This way, you can reassign the arguments without any problems.
    http://jsfiddle.net/Avorin/yZPj8/6/

  3. Posted April 10, 2011 at 10:49 pm

    When I want to pass a varying number of parameters to a function, I either use a predefined object or an object literal myself to begin with (I presume this example function is simplified).

    You can also clutter up the function calls with things like makePerson(null, “Joe”, 18) and test for nulls, too, instead of array lengths.

  4. Joe Fiscella
    Posted April 10, 2011 at 10:49 pm

    This is the solution I found, using this. instead of an args array. I’m not sure which solution is better.

    http://jsfiddle.net/Q2LMT/

  5. Posted April 10, 2011 at 10:49 pm

    Or simply refer to the arguments by name when changing their values.

  6. Bob Dole
    Posted April 10, 2011 at 10:49 pm

    This article roughly says:

    When you misuse the arguments object, unexpected results happen.

    The solution: don’t misuse the arguments object. Leave the param list empty and use your logic to fill out variable names if you need that

  7. Peter
    Posted April 10, 2011 at 10:49 pm

    This is why I love working with Rails… most Rails functions take hashes as arguments, so you can your real arguments in in any order, and it guarantees code verbosity. Example:

    button_to ‘Add to Cart’, line_items_path(:product_id => product), :remote => true

    where :remote=>true is the third argument, a hash, and contains all optional parameters you could add (in this case, :method, :disabled, :confirm, and :remote).

  8. Josh
    Posted April 10, 2011 at 10:49 pm

    var makePerson = function(favoriteColor, name, age) {
    if (arguments.length < 3) {
    favoriteColor = “green”;
    name = arguments[0];
    age = arguments[1];
    }

    return {
    name: name,
    age: age,
    favoriteColor: (arguments.length < 3 ? "green" : favoriteColor)
    };
    };

  9. Larry Clapp
    Posted April 10, 2011 at 10:49 pm

    How very Perl-ish of Javascript.

  10. Matt
    Posted April 10, 2011 at 10:49 pm

    Ignore this blog post’s advice. It is perfectly fine to reassign function arguments in Javascript. If you just follow the convention of putting option arguments at the end of the argument list instead of the beginning, you avoid this problem all together and simplify your code:

    var makePerson = function(name, age, favoriteColor) {
    favoriteColor = favoriteColor || “green”;
    return { name: name, age: age, favoriteColor: favoriteColor };
    };

  11. Posted April 10, 2011 at 10:49 pm

    Who makes the first argument optional? Seriously? There are numerous things wrong with your code.

  12. Gekkor McFadden
    Posted April 10, 2011 at 10:49 pm

    What a terrible programming language.

  13. Posted April 10, 2011 at 10:49 pm

    Larry Clapp, this isn’t perlish at all. In Perl you do named parameters through local variables. They’re duplicated not ref-copied.

    use strict;
    use warnings;

    my $makePerson = sub {
    my ( $favoriteColor, $name, $age ) = @_;

    if ( @_ < 3 ) {
    $favoriteColor = “green”;
    $name = $_[0];
    $age = $_[1];
    }

    return {
    name => $name
    , age => $age
    , favoriteColor => $favoriteColor
    }

    };

    use Data::Dumper;
    die Dumper $makePerson->(‘Joe’, 18);

    What you’re confusing is Perl’s special array variable `@_` which is used to store references to the parameters from the caller, making them accessible in the callee. So the sub implementation themselves are pass-by-reference, but the assignment itself requires a total copy. Not to say you couldn’t achieve the same effect with Perl if you *really wanted too*, but it requires a ton of non-accidental (contrived) work.

    use strict;
    use warnings;

    my $makePerson = sub {
    my ( $favoriteColor, $name, $age ) = ( \$_[0], \$_[1], \$_[2] );
    #my ( $favoriteColor, $name, $age ) = @_;

    if ( length @_ < 3 ) {
    $$favoriteColor = “green”;
    $$name = $_[0];
    $$age = $_[1];
    }

    return {
    name => $$name
    , age => $$age
    , favoriteColor => $$favoriteColor
    }

    };

    use Data::Dumper;
    my $name = ‘Joe’;
    my $age = 18;
    die Dumper $makePerson->($name, $age);

  14. jack
    Posted April 10, 2011 at 10:49 pm

    How about just using a configuration object?

    var person = makePerson({name:”Joe”, age:18})

    Inside the function look for the property you want to default.

  15. Posted April 10, 2011 at 10:49 pm

    JavaScript reveals more and more of its awful design. NaN != NaN ?????

  16. pwnedxx0r
    Posted July 8, 2011 at 12:07 pm

    the problem isn’t with using arguments, the problem is with your use of it.

    Writing the code:

    function example (x, y, z) {
    x = 1;
    y = arguments[0];
    z = arguments[1];
    }

    will make every value 1 because I wasn’t very careful about the order of my actions.

    As the article you quoted states, the variables x, y, and z are synonymous with arguments [0], [1], and [2] respectively, so if I called example(3,4) all I would be doing in my function is assigning 3 to x and 4 to y with the function call, then assigning 1 to x, the value of x to y, then the value of y to z. All of my values would be the same (and 1) afterwards.

    You do the same thing in your code. You pass in (favoriteColor: Joe, name: 18) and then set the favoriteColor to “green” before taking the value of “green” and pasting it on to the name, then taking the new value of name (also “green”) and pasting it in to the value of age. If you had instead written that code in the reverse order, it would have worked as you had initially expected.

One Trackback

  1. [...] service allows you to react immediately to spikes in website traffic. Just recently our blog had a post go viral on Reddit causing an extreme spike in traffic. Using a live information radiator on our office [...]