JS Optimisation #1 – For Loop

, ,

This tip shows how to maximise the efficiency of your for loops.

When writing for loops, the classic mistake novices make is not removing dereferences from the test condition. What am I talking about?! Okay, this might look like a pretty normal for loop to most people:

for (var i = 0; i < myArray.length; i++) {
    // ...do something
}

So what’s the problem?

Well, we have three statements going on here:

  1. The declaration and assignation of the indexing variable i, which occurs once.
  2. The test condition, which occurs for every iteration of the loop.
  3. The variable increment, which again occurs for every iteration of the loop.

The main reason this for loop is inefficient is that on every iteration, the value of myArray.length is recalculated. The browser has to take a step inside myArray to look for the property length before testing its value. The act of entering an object to access a property (or an array to access an item/property) is called a dereference and takes a certain amount of processing speed. So, the most performant way to speed this loop up is to remove the repetitive dereference and assign the array length to a variable first:

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

Sweet! So now the array length is calculated once at the beginning of the for loop and never again. That’s a nice little speed saving. Okay, so it’s a small saving, but if you had hundreds of items in your array, or perhaps your array was within an object itself so you had multiple dereferences e.g. myObject.myArray.length, or if you had many loops, it can start to add up and impact your code’s performance.

So our loop is now quicker, but can we optimise it further? Yes, but not much. Even so, this is where the code starts to get more interesting!

One thing we’ve added in our new version is an extra variable len. How about rejigging our loop so we only need one variable?

for (var i = myArray.length - 1; i > -1; i--) {
    // ...do something
}

So what’s going on here? To remove the second variable, we’re looping through the array backwards. We set the starting point as the last index of the array (myArray.length - 1), then keep looping backwards until we hit 0 (i > -1). Nice. Looping backwards isn’t always feasible however and depends very much on what you’re doing, but hey, it saves us an unnecessary variable!

Is that it? Nope, we can take it even further:

for (var i = myArray.length; i--;) {
    // ...do something
}

Woah! It looks cool, it looks slick, but what the hell is going on?! Okay, lets step through it so it makes a bit more sense.

Firstly we’re setting the indexing variable i to the length of the array. This is our starting point as we’re still looping backwards. Note how we’re not setting it to length - 1, so we’re effectively pointing at an index one greater than the last array item.

Now here’s the clever bit. The next section performs the test condition and reduces our variable by 1 at the same time! Confused? Lets break it down a bit further.

We need to reduce our variable i by 1 so we’re pointing at the last array item when we start, i-- does that. All good. But how is i-- a test condition?! This is where something funky about javascript comes into play called falsy.

When used as a boolean test, many different values in javascript evaluate to either true or false, including numbers (I’ll write a more detailed post on this subject at another time.) Anything from 1 and above evaluates to true, 0 evaluates to false. We harness this quirk by using it in the test condition section of our loop. On every iteration we reduce the i variable by 1 until it we hit 0, which evaluates to false, therefore exiting our loop. Simples! *squawk*

The for loop requires three separate sections, so it’s important to include the ; after i--. The last section is normally reserved for incrementing the indexing variable, but is now redundant to us as we’re doing this within the second section, so we use the ; to denote the end of the second section and leave the last section empty.

For the eagle-eyed developer, one question may still remain: If 0 evaluates to false, how is the last iteration in the loop ever run? Surely we’re finishing our loop 1 iteration too early?! That’s where a programming quirk comes into play. We’re decrementing the indexing variable by using i--. We could also write this as --i. What’s the difference? The answer is order. i-- means “use the value of variable i, then decrement it by 1,” --i means “decrement the value of variable i by 1, then use it.” In the context of our test condition, this means on the last iteration i is equal to 1, which evaluates to true and is then reduced to 0 before entering our for loop’s inner code. Oh yes.

This whole idea might take a bit of getting your head around, but if you spend some time considering all the points mentioned, you’ll speed up your code performance whilst also picking up a few bonus programming concepts at the same time, and most importantly, looking cool in front of other developers! Hooray for the super for loop!