views:

77

answers:

5

So I was having a debate with a fellow engineer about looping in JavaScript. The issue was about the native for loop construct and prototype's each() method. Now I know there are lots of docs/blogs about for and for-each, but this debate is somewhat different and I would like to hear what some of you think.

Let's take the following loop for example

example 1

var someArray = [blah, blah, blah,...,N];
var length = someArray.length;

for(var index = 0; index < length; index++){
    var value = someFunction(someArray[index]);
    var someOtherValue = someComplicatedFunction(value, someArray[index]);
    //do something interesting...       
}

To me, this comes second nature mainly because I learnt how to code in C and it has carried me through. Now, I use the For-each in both C# and Java (bear with me, I know we are talking about JavaScript here..) but whenever i hear for loops, i think of this guy first. Now lets look at the same example written using Prototype's each()

example 2

var someArray = [blah, blah, blah,…..,N];
someArray.each(function(object){
    var value = someFunction(object);
    var someOtherValue = someComplicatedFunction(value, object);
    //do something interesting...   
});

In this example, right off the bat, we can see that the construct has less code, however, i think each time we loop through an object, we have to create a new function to deal with the operation in question. Thus this would preform badly with collections with large number of objects. So my buddy's argument was that example 2 is much easier to understand and is actually cleaner than example 1 due to its functional approach. My argument is that any programmer should be able to understand example 1 since it is taught in programming 101. So the easier argument doesn't apply and example 1 performs better than example 2. Then why bother with #2. Now after reading around i found out that when the array size is small the overhead for example 2 is minuscule. However people kept on talking about the lines of code you write is less and that example 1 is error prone. I still don't buy those reasons, so I wanted to know what you guys think…

A: 

The answer is - its subjective.

For me, example 2 is not really THAT much less code, and also involves downloading (bandwidth) AND parsing/executing (execution-time) a ~30kb library before you can even use it - so not only is the method itself less efficient in and of itself, it also involves setup overhead. For me - arguing that example 2 is better is insanity - however that's just an opinion, many would (and are perfectly entitled to) disagree completely.

lucideer
as @warfangle mentioned, if the only desired functionality is the `Array.each` method, then just use that. There's no need to fetch the entire library for one function.
Anurag
+3  A: 

You are not creating a new function on each iteration in the second example. There is only one function that keeps getting called over and over. To clarify the point of only a single function being used, consider how would you implement the each method yourselves.

Array.prototype.each = function(fn) {
    for(var i = 0; i < this.length; i++) {
        // if "this" should refer to the current object, then
        // use call or apply on the fn object
        fn(this[i]);
    }
}

// prints each value (no new function is being created anywhere)
[1, 2, 3].each(function(v) { console.log(v); });

Sure, there is the overhead of calling a function in the first place, but unless you are dealing with massive collections, this is a non-issue in modern browsers.

Personally I prefer the second approach for the simple reason that it frees me from worrying about unnecessary details that I shouldn't be concerned about in the first place. Say if we are looping through a collection of employee objects, then index is just an implementation detail and in most cases, if not all, can be abstracted away from the programmer by constructs such as the each method in Prototype, which by the way is now a standard in JavaScript as it has been incorporated into ECMAScript 5th ed under the name forEach.

var people = [ .. ];
for(var i /* 1 */ = 0; i /* 2 */ < people.length; i++ /* 3 */) {
    people[i /* 4 */].updateRecords();
    people[i /* 5 */].increaseSalary();
}

Marked all 5 occurrences all i inline with comments. We could have so easily eliminated the index i altogether.

people.forEach(function(person) {
    person.updateRecords();
    person.increaseSalary();
});

For me the benefit is not in lesser lines of code, but removing needless details from code. This is the same reason why most programmers jumped on the iterator in Java when it was available, and later on the enhanced for loop when it came out. The argument that the same grounds don't hold for JavaScript just doesn't apply.

Anurag
+2  A: 

I'm fairly certain it does not create a new code function for every iteration, but rather calls the function on each one. It may involve a little more overhead to keep track of the iterator internally, (some languages allow the list to be changed, some don't) but it shouldn't be all that very much different internally than the procedural version.

But anytime you ask which is faster, you should ask, does it really matter? Have you put a code profiler on it and tested it with real world data? You can spend a lot of time figuring out which is faster, but if it only accounts for .0001% of your execution time, who cares? Use profiling tools to find the bottlenecks that really matter, and use whichever iteration method you and your team agree is easier to use and read.

DGM
You are right i wasn't really looking for performance reasons. It was just an educational debate...
The Code Pimp
+1  A: 

Example one is not only error prone, for arrays of trivial length it's a poor choice - and the each (or forEach, as defined in JavaScript 1.6, yet IE8 still does not support it) is definitely style-wise the better choice.

The reason for this is simple: you are telling the code what to do, not how to do it. In my tests with firefox, the forEach method is about 30% the speed as a for loop. But when you're doing miniscule arrays it doesn't even matter that much. Much better to make your code cleaner and easier to understand (remember: what it's doing instead of how to do it), for not only your sanity the next time you come back to it, but for the sanity of anyone else looking at your code.

If the only reason you're including prototype is for the .each method, you're doing it wrong. If all you want is a clean iteration method, use the .forEach method - but remember to define your own. This is from the MDC page on forEach - a useful check to give yourself a .forEach method if none exists:

if (!Array.prototype.forEach)
{
  Array.prototype.forEach = function(fun /*, thisp*/)
  {
    var len = this.length >>> 0;
    if (typeof fun != "function")
      throw new TypeError();

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
        fun.call(thisp, this[i], i, this);
    }
  };
}

You can tell from how this works, that a new function is not created for each item, though it is invoked for each item.

warfangle
I see where you are coming from but it is interesting that arrays of trivial length provides poor performance. Did you perform some profiling to come to this conclusion?
The Code Pimp
I used JSLint to benchmark. No matter the length of the array, performance of .each is about 30% of the performance of the loop. This, of course, is simply the overhead of the loop - all I was doing was incrementing a counter within the loop. The overhead of what you actually do within the loop is likely to be a much more apparent bottleneck in performance. If you iterate over extremely large collections, think about using setTimeout to iterate - it will perform even worse, but it will not lock up the browser as it iterates. (prevents "unresponsive script" errors due to how setTimeout works)
warfangle
A: 

IMO the second approach is succinct and easier to use, though it gets complicated (see prototype doc) if you want to use things like break or continue. See the each documentation.

So if you are using simple iteration, use of the each() function is better IMO as it could be more succinct and easy to understand, although its less performant than the raw for loop

naikus