Arguments, the Array that is not an Array

If you’ve work with JavaScript for long enough you will inevitably be forced to use the arguments local variable of a function. Please note that: “arguments as a property of Function can no longer be used.”. Let’s take a deeper look into what this arguments variable actually is, what it does, and how you should use it.

The arguments variable is the element that gives you access to all of the variables passed into your function without them having to specifically name those variables. This makes it incredibly useful once you start working with nested functions and callbacks. Now, the confusion starts when you begin to hear that despite looking like an Array the arguments variable is not actually an Array, but an Array-Like object. This confusion tends to stem from the fact that if you console out arguments you get something that looks a lot like an array.

var func = function(){
  console.log(arguments);
};

func(2, 3, 4); //[2,3,4]

So what does that mean for you? Well it means that all of the following examples would not work:

var func = function(){
  var args = arguments.slice();
  console.log(args);
};

func(2, 3, 4); //TypeError: undefined is not a function

var func = function(){
  var arg = arguments.pop();
  console.log(arg);
};

func(2, 3, 4); //TypeError: undefined is not a function

The reason for this is that even though arguments may look like an Array, it is not an Array and does not the same properties and methods that an array would have. The only properties that arguments does have is .length and the ability to access variables inside is with the bracket notation “arguments[0];”. If you’ve read my blog post on JavaScript’s array object you might have an idea of what’s going on here. In that blog post I explain how Array objects in JavaScript are wrappers around a native C array which provides us with the properties and methods we need to interact with the array. The same thing happens with arguments. The arguments element still uses a native C array to store its data, but it has a different wrapper with different properties and methods. Because the underlying data storage is still the same, you have some workarounds that you can use in order to apply other Array methods to arguments. Here’s couple of examples of how to do so:

var func = function(){
  var args = Array.prototype.slice.call(arguments);
  console.log(args);
};

func(2, 3, 4); // [2,3,4]

var func = function(){
  var arg = Array.prototype.pop.call(arguments);
  console.log(arg);
};

func(2, 3, 4); // 4

Despite having shown you these examples, I am going to strongly recommend that you do not use them. In order to understand this better we need to understand something about most JavaScript engines and V8 in particular. For this I will be taking an excerpt from https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments:

“In V8 there is no interpreter but there are 2 different compilers: generic and optimizing. That means your Javascript is always compiled and run directly as native code. That means It's fast, right? Wrong. Code being compiled to native code by itself isn't hugely significant for performance. It just removes interpreter overhead but the code will still be slow if not optimized.

For example in generic compiler a + b will become something like this:

mov eax, a
mov ebx, b
call RuntimeAdd

In other words it just calls a runtime function. If a and b will always be integers, then something like this:

mov eax, a
mov ebx, b
add eax, ebx

would perform massively faster than the call figuring out complex JavaScript addition semantics at runtime.”

If you would like to do some more reading on V8 optimization and the difference between the generic and optimizing compiler I would recommend reading the entire blog post. But how does this affect the arguments variable? The problem is that once you modify the arguments variable in any way that is not arguments.length or arguments[i] (note that i must be a valid index) then the function will not be able to be run by the optimized compiler and it will run significantly slower. Here’s another example of direct manipulation of arguments and a workaround that does not manipulate it directly. An example of this would be reassigning a defined parameter while also mentioning arguments in the body. Typical example:

function defaultArgsReassign(a, b) {
     if (arguments.length < 2) b = 5;
}

Workaround is to save the parameter to a new variable:

function reAssignParam(a, b_) {
    var b = b_;
    //unlike b_, b can safely be reassigned
    if (arguments.length < 2) b = 5;
}

I hope this has helped to clear up some misunderstandings of the arguments variable in JavaScript and will help you use it more efficiently and confidently.

Paulo Diniz