Posts tagged with 'extend'

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)


Extras

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

May 2012 (4)
SMTWTFS
« Apr  
12345
6789101112
13141516171819
20212223242526
2728293031

Like this Archive Calendar widget? Download it here.

Social networking

The OUT Campaign

The OUT Campaign

My Tweets

  • Honest Movie Trailer of Phantom Menace http://t.co/sif8y4Ns and then Battleship, er, Transformers http://t.co/sif8y4Ns
  • Damn, Donna Summer and Chuck Brown both gone in the last 24 hours. Different types of music, sure, but enjoyed them both. :(
  • Just saw a company page showing a list of tweets with "Join the conversation" linked to their Twitter a/c. The tweets are 6 months old #fail
Bottom swirl