By

Tales from the gist: 'for loopiness'

This is the first in a series of X articles discussing Rebecca Murphey's gist challenge for javascript developers, highlighting some of the things I learned in the process of answering and checking against others' solutions. See Rebecca's followup post for more details.

NOTE: the title is a vague/lame pun on a formerly popular TV series, in honor of Halloween. If you didn't notice, you get bonus points.

Check out my initial response to see how I did, compared to others. We'll kick off the discussion with a subtle (but important) difference in how I set up my looping constructs, and the effect those differences might have on the surrounding code:

The for...in trap (Questions 4 & 5)

I made the same mistake in both questions, but we'll look at #5 first. Given this challenge:

// 5: given the following array, create an array that contains
// the contents of each array item repeated three times ...
var myArray = [ 'foo', 'bar', 'baz' ];

Simple, right? I came up with this solution:

// answer
var newArray = [];

for (el in myArray) {
    newArray.push(myArray[el] +' '+ myArray[el] +' '+ myArray[el]);
}

It works. Cool. Then, But the judges (actually it was just me looking it up on Mozilla Developer Center [MDC]) slapped a giant warning on my use of the for...in loop:

"Although it may be tempting to use this as a way to iterate over an Array, this is a bad idea. The for...in statement iterates over
user-defined properties in addition to the array elements, so if you modify the array's non-integer or non-positive properties
(e.g. by adding a "foo" property to it or even by adding a method or property to Array.prototype), the for...in statement will return
the name of your user-defined properties in addition to the numeric indexes. Also, because order of iteration is arbitrary,
iterating over an array may not visit elements in numeric order."

But we haven't altered the Array prototype, or added anything to the object aside from normal array items. True, but I get the point. We need to get defensive here. If this were part of a larger application, or your new favorite javascript library decides to extend the Array object, then your code would break. You know, "an ounce or prevention" and all that jazz. The solution is simple: just use the plain ol' vanilla for loop. Done:

// new answer
var newArray = [];

for (var i=0; i < myArray.length; i++) {
    newArray.push(myArray[i] +' '+ myArray[i] +' '+ myArray[i]);
}

Question #4 was equally simple, but with a very similar trap:

// 4: given the following code, and assuming that each defined
// object has a 'destroy' method, how would you destroy all of
// the objects contained in the myObjects object?
var myObjects = {
  thinger : new myApp.Thinger(),
  gizmo : new myApp.Gizmo(),
  widget : new myApp.Widget()
};

Easy:

// my original answer
for each (obj in myObjects) {
    if (typeof obj === 'object')
        obj.destroy();
}

Again, it works fine. One major difference is that I used the for each...in loop instead of for...in. That means I'm working with the actual object on each iteration instead of just the property names. I'm not sure if there are any other differences, so I'll leave that as an exercise for the reader. No, seriously, drop some knowledge on me.

However, I opted to switch mine to the good ol' for...in loop to keep things simple, and to benefit from the collective insight of other solutions. Here's my new answer:

// my new answer
for (prop in myObjects) {
    if (myObjects.hasOwnProperty(prop))
        myObjects[prop].destroy();
}

Notice the introduction of the hasOwnProperty method. According to the MDC: "unlike the in operator, [the hasOwnProperty] method does not check down the object's prototype chain." So, by adding this check into our loop we can be sure we're only working with the properties we want. Bingo.

As Rebecca points out in her post, hasOwnProperty might be overkill, but it really can't hurt to have that protection.

Conclusion:

Both of my solutions gave the desired output in a controlled context, but of course there are ways I could improve them. By taking some of the common precautions outlined in the other submitted solutions (gotta love the fork-a-gist format), we can probably prevent some maintenance headaches. Oh, and maybe I should go back and re-read the "Pragmatic Paranoia" chapter of one my all-time favorite programming books, for a little refresher.