36

I was browsing around and I found this:

var i, len;
for(i = 0, len = array.length; i < len; i++) {  
   //...
}

My first thoughts are:

  • Why he did that? (it must be better for some reason)
  • Is it worth it? (I assume yes, why else he will do it this way?)

Do normal loops (the ones that don't cache the length) check the array.length each time?

ajax333221
  • 11,436
  • 16
  • 61
  • 95
  • 3
    Caching the length is probably a bigger deal in older browsers that don't compile the code. If I remember, Chrome pretty well optimizes this away. – RightSaidFred Dec 09 '11 at 22:11
  • 1
    @RightSaidFred - Chrome doesn't optimize it away because it can't (the length could change inside the loop), but it's just fast enough that there isn't a big speed difference either way. – jfriend00 Dec 09 '11 at 22:21
  • @jfriend00: Because it is compiled code, it would seem that Chrome could know if the Array is modified in the loop, and optimize if not. But I certainly agree that it's basically a moot point when it comes to Chrome. – RightSaidFred Dec 09 '11 at 22:22
  • @RightSaidFred - Maybe if there were no function calls in the loop and no properties with accessors in the loop and no methods on the array called in the loop, etc... But, it's pretty hard in JS to know what might or might not modify the length of the array indirectly. I think Chrome is just fast enough that these types of optimizations don't make as much of a difference as they used to. – jfriend00 Dec 09 '11 at 22:25
  • @jfriend00: Yeah, I totally hear what you're saying. I've often wondered just how far Chrome is able to follow the code and optimize. The few times I've run code through an Advance Optimization in Google Closure Compiler, I was pretty amazed at the code it unraveled. Not sure how that plays in though. – RightSaidFred Dec 09 '11 at 22:56

6 Answers6

30

A loop consisting of three parts is executed as follows:

for (A; B; C)

A - Executed before the enumeration
B - condition to test
C - expression after each enumeration (so, not if B evaluated to false)

So, yes: The .length property of an array is checked at each enumeration if it's constructed as for(var i=0; i<array.length; i++). For micro-optimisation, it's efficient to store the length of an array in a temporary variable (see also: What's the fastest way to loop through an array in JavaScript?).

Equivalent to for (var i=0; i<array.length; i++) { ... }:

var i = 0;
while (i < array.length) {
    ...
    i++;
}
Community
  • 1
  • 1
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • the problem is the property lookup. In order to calculate expression `B`, its necesarry to have one lookup on the object/Array for each iteration, that is slower than having a lookup with the local scope. Its always the fastest possible way to lookup something if it can get found in the local scope, that is why you would store the length in the current scope by using `var foo =`. – jAndy Dec 09 '11 at 22:18
  • 1
    The difference is very dependent on the browser. In older browsers, caching is more effective. For comparisons, have a look at: http://jsperf.com/caching-array-length/4 – Rob W Dec 09 '11 at 22:22
  • @RobW: certainly yes. new'ish js engines will optimize object lookups like a lot. The engine will have internal lookup hashes to access a property. So yes, this micro optimazation will have much more effect on oldish browsers (=== IE<9) – jAndy Dec 09 '11 at 22:24
13
Is it worth it? (obviously yes, why else he will do it this way?)

Absolutely yes. Because, as you say, loop will calculate array length each time. So this will cause an enormous overhead. Run the following code snippets in your firebug or chrome dev tool vs.

// create an array with 50.000 items
(function(){
    window.items = [];
    for (var i = 0; i < 50000; i++) {
        items.push(i);
    }
})();

// a profiler function that will return given function's execution time in milliseconds
var getExecutionTime = function(fn) {
    var start = new Date().getTime();
    fn();
    var end = new Date().getTime();
    console.log(end - start);
}

var optimized = function() {
    var newItems = [];
    for (var i = 0, len = items.length; i < len; i++) {
        newItems.push(items[i]);
    }
};


var unOptimized = function() {
    var newItems= [];
    for (var i = 0; i < items.length; i++) {
        newItems.push(items[i]);
    }
};

getExecutionTime(optimized);
getExecutionTime(unOptimized);

Here is the approximate results in various browsers

Browser    optimized    unOptimized
Firefox    14           26
Chrome     15           32
IE9        22           40
IE8        82           157
IE7        76           148 

So consider it again, and use optimized way :)
Note: I tried to work this code on jsPerf but I couldn't access jsPerf now. I guess, it is down when I tried.

Fatih Acet
  • 28,690
  • 9
  • 51
  • 58
  • 2
    You should include the version of Firefox and Chrome. This is a very detailled JSPerf: http://jsperf.com/caching-array-length/4 – Rob W Dec 10 '11 at 10:22
1

I always thought in JavaScript length was just a property of the array object, pre-calculated by previous array operations - creation, addition, removal - or overridden by the user, so you're just looking up a variable anyway? I must admit I had just assumed that because of the lack of parenthesis, but looking at the MDN page for array.length, it seems to say the same thing.

In languages where length is a method or length is calculated by a standard library function, then you should pre-calculate the length before running the loop so The array isn't calculated every iteration, particularly for large datasets. Even then, in modern high level languages like Python, len() just returns the length property of the array object anyway.

So unless I'm mistaken, the complexity is just O(1), and from that standpoint, even if the variable were slightly faster than a property to lookup each pass, it wouldn't be worth the potential trouble of creating/reusing additional variables outside of the protective for loop scope.

However, I suspect that in this case the reason the example's programmer chose this approach is simply just a habit they picked up in another language and carried forwards JavaScript.

Jim50
  • 438
  • 5
  • 5
0

One reason to do this is say, if you're adding elements to the array during the loop but do not want to iterate over them. Say you want to turn [1, 2, 3] into [1, 2, 3, 1, 2, 3]. You could to that with:

var initialLength = items.length;
for(var i=0; i<initialLength; ++i) {
    items.push(items[i]);
}

If you don't save the length before the loop, then array.length will keep increasing and the loop will run until the browser crashes / kills it.

Other than that, as the others said, it mildly affects performance. I wouldn't make a habit out of doing this because "premature optimization is the root of all evil". Plus, if you change the size of the array during the loop, doing this could break your code. For instance, if you remove elements from the array during the loop but keep comparing i to the previous array size, then the loop will try to access elements beyond the new size.

Domino
  • 6,314
  • 1
  • 32
  • 58
0

Yes Its check the array.length every time, but if we don't want to run our loop for increased array then, in that case, we can use forEach method.

forEach is a javascript in build method which is does like for loop, but forEach only iterative the elements in an array which are before the loop start.

Let's understand this from below snippet:

array = [1,2,3,4]

for(i=0;i<=array.length;i++){
console.log(array[i]);
array.push(array[i]+1);
}

output like 1 2 3 4 5 6 ...so on (infinite) while its checking array.length each time

let's check with forEach method

array = [1,2,3,4]
array.forEach((element) => {
console.log(element);
array.push(element+1);
})
console.log("array elements after loop",array);

it only processes for 4 elements which are present in array before iteration start.

**but in forEach case, it will affect on array.length if we pop out the element from array.

lets see this by an example:

array = [1,2,3,4]
array.forEach((element) => {
console.log(element);
array.pop()
})
console.log("array elements after loop",array);
-1

Here are a number of performance tests for different approaches

http://www.websiteoptimization.com/speed/10/

Mad Man Moon
  • 741
  • 4
  • 10