Thursday 26 July 2012

JavaScript arguments

I've come across lately with another amazing JavaScript feature, and it's not a ECMAScript 5 novelty or ES6 proposal, it's something that exists in the language since its inception, the arguments array-like object.

OK, sure you (as me) have used it previously, so what's the fuzz with it? well, the thing is that I used to think of arguments as an array of references to the objects referenced by the local variables. That means that it's good for read access, but if I wanted to assign a new value to let's say arguments[0], arguments would point to that new object, but the corresponding variable would still point to the initial object... Well, that's not the case, we should better think of arguments as a (sort of) Array of references to references to objects (think in C#'s ref keyword). This means that if we make a local variable point to a different object, its corresponding arguments[i] will correctly point to that object, and vice versa.

Let's see an example:

function parseTexts(txt, person){
 txt += " modified";
 console.log(arguments[0]);
 console.log(txt);
 
 arguments[0] = arguments[0] + " modified2";
 console.log(arguments[0]);
 console.log(txt);
 
 //quite astonishing, even passing the arguments pseudo array to another function the changes "replciate"
 (function(args){
  args[0] += " modified3";
  args[1] = {name: "xuan 2"};
 }(arguments));
  arguments[0] = arguments[0] + " modified2";
 console.log(arguments[0]);
 console.log(txt);
 console.log(arguments[1].name);
 console.log(person.name);
}

If you run the code above you'll see that arguments[0] and txt continue to point to the same object, regardless of which one you used for the reassignation.

I suddenly realized this could be pretty useful to fix a minor annoyance I'm faced with quite often. As you know, JavaScript lacks default parameters, so we end up writing code like this:

function Person(name, lastname){
name = name || "John";
lastname = lastname || "Doe";
}

Well, if we're dealing with a long number of arguments that becomes boilerplate, so, let's use arguments, as we've seen that if we change where it points to, we'll be in fact changing where the local variable points to:

function setDefaults(defaults, params){
 defaults.forEach(function(it, i){
  if (params[i] === undefined)
   params[i] = it; 
 });
}

function Person2(name, age, city){
 setDefaults(["xuan", 3, "Xixón"], arguments);
 
}

A common alternative to the solution above is using the $.extend - options object combination commonly used in jQuery plugins:

function Person2(options){
options = $.extend({
name: "xuan",
age: 3,
city: "Xixón"
}, options);

}
Person2({name: "xuan"})

The obvious drawback here is that instead of working directly with the parameters you'll have to use properties in a single local variable.

Update, 2013/02/07:
Unfortunately it seems like the beautiful arguments pseudo array will be getting less and less love in the near future. First, if working on strict mode, it will no longer be "live bound" to the variables, so that all the magic that I described above will be gone, if you modify the variable, the bucket in the arguments pseudo array will continue to point to the old value, and vice versa. You can read more in the "Making eval and arguments simpler" section here
Furthermore, as explained here it seems like there are plans to add new simplified constructs to the language that will end up replacing arguments

No comments:

Post a Comment