Posts tagged with 'arguments'

JavaScript for C# developers: converting arguments into an array

Way back in March 2009, I wrote a quick post about the JavaScript arguments quasi-array, about how the interpreter sets it up on every call to every function to hold the arguments passed to that function.

Brown paper wrapped parcelAlthough I blithely talked about the fact that arguments was array-like, it wasn’t a real array (it’s not typeof Array) and didn’t have methods like pop() and the like. In fact, it only really has a length property and a bunch of properties named ‘0’, ‘1’, ‘2’ and so on. So it’s easy to iterate over the elements, er, properties, but that’s essentially it.

It’s actually quite easy to convert it to an array and then use that. You don’t even have to use the obvious for loop to do it either. You use the Array’s prototype’s slice() method:

var argsArray = Array.prototype.slice.call(arguments);

Since arguments does not have the slice() method, we have to use the call() function to call it on arguments (this is an application of the apply invocation). slice() returns part of an array as an array, but If you don’t pass any arguments to it – start index, end index – the function returns all of the elements instead. Which is, if you think about it, what we want.

So, if you have, say, a logging library for your code, and you want to log the values of the arguments to a particular function, you could do something like this:

var convertArgs = function (args) {
  return Array.prototype.slice.call(args);
};

var myLoggingLibrary = {
  logArguments: function (argsArray) {
    console.log(argsArray);
  } 
};

var someFunctionToTest = function (a, b, c, d) {
   // do something with the arguments; we’ll just print the first
   console.log(a);
};

var addArgumentsLogging = function (f) {
  return function () {
    var args = convertArgs(arguments);
    myLoggingLibrary.logArguments(args);
    f.apply(this, args);
  };
};

someFunctionToTest(1, 2, 3, false); // prints "1"

someFunctionToTest = addArgumentsLogging(someFunctionToTest);

someFunctionToTest(1, 2, 3, false); // prints "[1, 2, 3, false]" then "1"

With the addArgumentsLogging() function, I am not altering the function to be logged: I am merely creating a wrapper around it that does the logging and then calls the original function using the apply invocation. Notice how I’ve replaced the original function with the wrapped version. (Note also that the addArgumentsLogging function creates a closure over the parameter f.)

So there you go: how to convert the arguments quasi-array into a real array and then use it.

Album cover for Goodbye Country (Hello Nightclub)Now playing:
Groove Armada - Little By Little
(from Goodbye Country (Hello Nightclub))


JavaScript: Using the shift method on the arguments array

In my webinar today on reading jQuery source code, I was talking about the extend method. I started out by talking about what a typical extend method would do before going onto the jQuery-specific one.

dominosphoto © 2008 greg westfall | more info (via: Wylio)A while back I wrote a simple one, more as a pedagogical exercise, rather than any real attempt to create the be-all and end-all version:

var extend = function () {
  var obj,
      name,
      shift = Array.prototype.shift,
      target = shift.call(arguments);

  target = target || {}; 
  obj = shift.call(arguments);
  while (obj) {
    for (name in obj) {
      if (obj.hasOwnProperty(name)) {
        target[name] = obj[name];
      }
    }
    obj = shift.call(arguments);
  };
  return target;
};

So, essentially, the code assumes that the first argument to the function is the target object and there will be zero or more source objects defined that will provide properties to the target object. The source objects are peeled off the arguments array one by one. The target object is returned (and of course is modified by the function).

Here’s an example of it in action (we extend an empty object with some default values and then some changed values):

var defaultOptions = { animate: true, color: "red" };
var myOptions = { color: "blue" };
var actualOptions = extend({}, defaultOptions, myOptions);
// result: { animate=true, color="blue"}

Anyway, this is not whether this code is correct or not, but about my first attempt at writing it. I initially had something like this:

var extend = function (target) {
  var obj,
      name,
      shift = Array.prototype.shift;

  obj = shift.call(arguments);
  obj = shift.call(arguments);
  while (obj) {
    for (name in obj) {
      if (obj.hasOwnProperty(name)) {
        target[name] = obj[name];
      }
    }
    obj = shift.call(arguments);
  };
  return target;
};

I was assuming that there was always a target object, so I declared it in the function declaration itself. I then shifted off (and discarded) the first element of the arguments array (which was the target object) and from then on, it was the same as before. Except: it didn’t work. What the heck was going on?

To help resolve the problem, I wrote the following code:

var cl = function (obj, text) {
  if (text) { console.log(obj); }
  else { console.log('<' + obj + '>'); }
};

var arraycl = function (a) {
  for (var i = 0; i < a.length; i++) {
    cl(a[i]);
  }
  cl('length: ' + a.length);
};

var f = function (a, b, c) {
  cl('===before manipulation', true);
  cl(a);
  cl(b);
  cl(c);
  cl('arguments array', true);
  arraycl(arguments);

  cl('===remove first argument', true);
  Array.prototype.shift.call(arguments);
  cl(a);
  cl(b);
  cl(c);
  cl('arguments array', true);
  arraycl(arguments);
};

f("one", "two", "three");

Ignore the first two functions there, they’re just helper functions. The real meat is in the f function. It uses three parameters, a, b, and c. It first logs the values of those parameters, and then the elements of the arguments array. All well and good, and here is the result of that part of the f function:

===before manipulation
<one>
<two>
<three>
arguments array
<one>
<two>
<three>
<length:   3>

This is exactly what I’d expect: the named parameters have the correct values from the call site and the arguments array reflects that. No sweat.

Now the f function shifts off the first parameter of the arguments array. Here is the result of that:

===remove first argument
<two>
<three>
<three>
arguments array
<two>
<three>
<length: 2>

First thing to note is that the arguments array has lost its first element and hence now has length 2. The next really wacky thing is that removing the first element from the array has actually changed the values of the named parameters, a and b. a now has the value of the second parameter passed in, and b the value of the third. c on the other hand still has the value of the third parameter and has not been set to undefined, for example. Hence, sometimes the named parameters reflect the current version of the arguments array, and ... sometimes not.

The lesson to be learned here is that modifying the arguments array in a function will also modify the values of the named parameters as well. This seemed counter-intuitive to me at first glance (hence my first cut at an extend function where I assumed that the named parameters kept their values even though I was manipulating the arguments array).

My recommendation is, if you are going to change the arguments array inside a function, don’t use named parameters as well.

Album cover for Be Yourself TonightNow playing:
Eurythmics - Sisters Are Doin' It for Themselves
(from Be Yourself Tonight)


JavaScript for C# programmers: the arguments array

A slight diversion in our quest to learn JavaScript from a C# programmer's perspective.

This episode is about the arguments array.

In C#, if we want to write a method that takes an undefined (at least at compile-time) number of parameters, we use the params keyword and declare a parameter array.

    static void ListArgs(params string[] args) {
      Console.Write(string.Format("There are {0} parameters: ", args.Length));
      for (int i = 0; i < args.Length; i++) {
        Console.Write(args[i] + " ");
      }
      Console.WriteLine();
    }

    static void Main(string[] args) {
      string[] words = {"one", "two", "three"};

      ListArgs(words);
      ListArgs("hello", "world");
      ListArgs();

      Console.ReadLine();
    }

Here we have a ListArgs method whose only parameter is a parameter array of strings, called unimaginatively args. The method merely outputs the number of parameters, and the values of each of them. The Main method calls this method in three different ways, first with an array of strings, then with two string parameters, then with nothing at all. In the first case, the compiler ensures the method is called with the given array. In the second case, the compiler inserts code to create an array containing the two parameters, and then calls ListArgs. In the third case the compiler creates an empty array and passes it in.

In all cases, the output is as we expect.

In JavaScript, no matter how we declare the parameters to a function, when we call it we always get a variable called arguments automatically created for us that contains all the parameters passed in.

One reason for this is that JavaScript is very loose in how it treats parameters. If you declare a function with two parameters, say, you can call it with exactly two parameters as you'd expect, but you can also call it with fewer than two parameters, or with more than two parameters. In the first case, any parameters that were not provided by the caller will be set to undefined inside the function; in the second case, the extra parameters are just ignored.

Nevertheless, in that second case, we can discover the extra parameters by using the arguments array. JavaScript will construct this pseudo-array to contain all the parameters passed to the function.

Here's the JavaScript equivalent of the ListArgs method:

var ListArgs = function() {
    var logstring = "There are " + arguments.length + " parameters: ";
    for (var i = 0; i < arguments.length; i++) {
        logstring += arguments[i] + " ";
    }
    console.log(logstring);
};

ListArgs("hello", "world");
ListArgs();

The call with a string array doesn't really translate: we'd have to check internally to the function to see if a particular parameter were an array and deal with it accordingly. Anyway, you can see that ListArgs is defined not to accept any formal parameters at all. Nevertheless, we can get at them by using the arguments pseudo-array.

I want to stress that arguments, although it looks like an array, isn't. It's an object, certainly, and it has a length property and its properties are numbers sequentially increasing from '0', but that's only as deep as the deception goes. A lot of times, it doesn't really matter that arguments is not an array (those properties I mentioned are usually enough to be getting on with), but sometimes you'll find yourself caught out and need some array methods. In that case you'll have to copy the elements to a real array, or you'll have to use the apply method to use an array method on the arguments object.

Because arguments (and this for that matter) are created when a function is called, they are locked in scope to that call of that function. They do not participate in the normal function scoping. Which is obvious, if you think about it: if you have a function B nested in function A, then any reference to arguments in B will use B's arguments object and not A's. If you want to share A's arguments with B, then you'll need to copy them to a local variable in A.

Album cover for (the best of) New Order Now playing:
New Order - World
(from (the best of) New Order)


Search

About Me

I'm Julian M Bucknall, the M because it's my middle initial and because I and the other Julian Bucknall (the movie guy) would like to differentiate ourselves.

I'm a programmer by trade, an actor by ambition, and an algorithms guy by osmosis. I write articles for PCPlus in my spare time, not that there's much of that.

Julian M Bucknall Apart from that, an ex-pat Brit, atheist, microbrew enthusiast, Pet Shop Boys fanboy, slide rule and HP calculator collector, amateur photographer, Altoids muncher.

DevExpress

I'm Chief Technology Officer at Developer Express, a software company that writes some great controls and tools for .NET and Delphi. I'm responsible for the technology oversight and vision of the company.

Validation

Validate markup as HTML5 (beta)     Validate CSS

Bottom swirl

Archives

February 2012 (4)
SMTWTFS
« Jan  
1234
567891011
12131415161718
19202122232425
26272829

Like this Archive Calendar widget? Download it here.

Social networking

Google ads

The OUT Campaign

The OUT Campaign

My Tweets

Bottom swirl