The for loop in JavaScript

The for loop is most commonly used for iterating over an array and performing an action on each item. For example:

var brothers = ['Reuben', 'Simeon', 'Levi'];

for(var counter = 0; counter < brothers.length; counter++) {
    console.log('Hello ' + brothers[counter] + '!');
}

In JavaScript the three parts in the parentheses are known as initialization, condition and final-expression, and perhaps surprisingly, they are all optional. The part inside the curly braces is the statement.

The steps taken when a for loop runs are:

  1. The initialization is evaluated.
  2. The condition is checked to see if it evaluates to true or false.
  3. If it’s false everything stops, if it’s true the statement is run once.
  4. The final-expression is run.
  5. Go back to 2.

Initialization

initialization is an expression or a list of variable assignments. Typically, and in our example above, it’s used to set the initial value of a counter. Because arrays are zero indexed, the counter is usually started at 0 when looping over an array from the start.

(Typing “counter” or something similar every time would get a bit tedious so developers tend to just use i.)

The counter can just as easily be started at the end of the array though. If you wanted to reverse the values in an array and didn’t know about array.prototype.reverse() one possible way to do it would be like so:

var brothers = ['Reuben', 'Simeon', 'Levi'];

var reverse = function(arr) {
    var i = 0,
        counter = 0;

    for(var ii = arr.length; ii > i; ii--) {
        var tmpValue = arr.pop();
        arr.splice(counter, 0, tmpValue);

        counter++;
    }

    return arr;
};

console.log(reverse(brothers));

In this example we are starting the loop counter at the number of values in the array. Each time the loop goes round it decrements the value stored in ii and increments the value stored in counter.

initialization doesn’t have to be a single variable assignment. If we look at our brothers example again we can see that each time the loop goes round it recalculates brothers.length as part of the conditional. This is potentially a waste of time if the array is very large, why not just assign the value of brothers.length to a variable?

var brothers = ['Reuben', 'Simeon', 'Levi'];

for(var i = 0, brothersCount = brothers.length; i < brothersCount; i++) {
    console.log('Hello ' + brothers[i] + '!');
}

We could also declare the variables outside the loop and take advantage of initialization being optional:

var brothers = ['Reuben', 'Simeon', 'Levi'],
    i = 0,
    brothersCount = brothers.length;

for(; i < brothersCount; i++) {
    console.log('Hello ' + brothers[i] + '!');
}

Now, remember I said initialization can be an expression? Well it can be any expression with one restriction: it can’t use the in operator. The spec uses the keywords ExpressionNoIn and VariableDeclarationListNoIn to describe what initialization can be.

To see why, we need to look at what the in operator does. It’s an operator that returns true if what is on the left of it is in what is to the right of it. In the case of arrays it returns true if the specified index is in the specified array.

For example in our brothers array there are 3 items so the indices are 0, 1 and 2. Therefore 2 in brothers returns true and 3 in brothers returns false.

However, there is also a for-in loop that uses the in operator. It is typically used to iterate over properties in an object like so:

var brothers = {
    first: 'Reuben',
    second: 'Simeon',
    third: 'Levi'
}

for(var position in brothers) {
    console.log('Hello ' + brothers[position] + '!');
}

Let’s imagine we don’t know what the first value in the array is but we want to get it. Using our knowledge of in in the context of arrays (and ignoring the simple array[0]) we might try:

for(var arrayHasValues = 0 in brothers; arrayHasValues; arrayHasValues = false) {
    console.log('Hello ' + brothers[0] + '!');
}

The thinking here is that we are using var arrayHasValues = 0 in brothers to check if the array has at least one value. If it has, arrayHasValues is true, the loop runs, then it’s stopped by final-expression setting arrayHasValues to false.

Just to reiterate, using the in operator this way is absolutely fine if it’s not in initialization. Try running the following and you’ll see it returns true:

var brothers = ['Reuben', 'Simeon', 'Levi'];

var arrayHasValues = 0 in brothers;

console.log(arrayHasValues);

The problem is that the in operator in initialization the way we’ve used it is checking for undefined in brothers as if it’s a for-in loop, so it doesn’t work. The spec has made it invalid and it will return a console error if we run it that way. A good job too because it’s mighty confusing for me!

To fix the loop so that it does get the first value in an array just wrap the expression that uses the in operator in parenthesis. for(var arrayHasValues = (0 in brothers); arrayHasValues; arrayHasValues = false) will work.

Condition

The second part of the for loop is condition. It’s an expression that is evaluated before each iteration, and if it evaluates to true the loop runs, if it’s false the loop doesn’t run.

condition is optional and if it’s omitted the loop runs as if it’s true. So for(; true; i++) is the same as for(; ; i++). We could use that in our original example, along with the break keyword that stops the loop.

var brothers = ['Reuben', 'Simeon', 'Levi'],
    i = 0;

for(; ; i++) {
    console.log('Hello ' + brothers[i] + '!');

    if(i === brothers.length - 1)
        break;
}

As an aside, there’s also a continue keyword that stops the rest of the statement being executed in the iteration where continue works, but the loop carries on. In the following example all the numbers between 0 and 29 except multiples of 7 are output to the console.

for(var i = 0; i < 30; i++) {
    if(i % 7 === 0)
        continue;
    
    console.log(i);
}

Final-expression

The last part inside the parentheses is final-expression, the expression that is evaluated after the statement in each iteration of the loop. Usually it’s the counter increment, but we could swap it with part of the statement. Let’s say we want to add the items in an array to an object we could do something like:

var brothers = ['Reuben', 'Simeon', 'Levi']
    i = 0,
    obj = {};

for(; i < brothers.length; obj[i] = brothers[i - 1]) {
    i++;
}

console.log(obj);

Remember, final-expression is just an expression. It doesn’t have to be a counter, that’s just the most common use. Remember also that it too is optional, so we can change our for loop again to:

var brothers = ['Reuben', 'Simeon', 'Levi'],
    i = 0;

for(;;) {
    if(i === brothers.length)
        break;

    console.log('Hello ' + brothers[i] + '!');

    i++;
}

Empty statement

Believe it or not the statement itself can be empty, as long as there’s something in final-expression that makes condition false at some point. For example we might want to add our brothers' last names into each item in the array and we could do it like so:

var brothers = ['Reuben', 'Simeon', 'Levi'];

for(var i = 0, ii = brothers.length; i < ii; brothers[i++] += ' Jameson');

console.log(brothers);

When using the empty statement it’s important to remember the semi-colon at the end or whatever comes next will be treated as the statement. In the previous example it’s console.log(brothers). The reason for this is that the curly braces are optional if the statement only takes up one line.

The following two badly indented code blocks produce different outputs:

var brothers = ['Reuben', 'Simeon', 'Levi'];

for(var i = 0, ii = brothers.length; i < ii; i++)
brothers[i] += ' Jameson';
console.log(brothers);
var brothers = ['Reuben', 'Simeon', 'Levi'];

for(var i = 0, ii = brothers.length; i < ii; i++) {
brothers[i] += ' Jameson';
console.log(brothers)
}

A quick word on optimisation

I touched on this earlier when we looked at initialization and brothers.length being calculated each time the loop goes round. Moving it out of the loop and assigning it to a variable means the work involved it counting the length of the brothers array only happens once.

This idea of moving code out of the loop that doesn’t change is called loop-invariant code motion. There’s a nice example of loop-invariant code motion on jsPerf where you can see the benefits of moving loop-invariant code out of a loop.

So next time you find yourself writing something like:

for(var i = 0; i < len; i++) {
    if(foo[bar] === baz) {
        // do something
    }
}

change it to:

if(foo[bar] === baz) {
    for(var i = 0; i < len; i++) {
        // do something
    }
}

That way the expression foo[bar] === baz is only evaluated once.

If you want to get really into loop optimisation there’s some interesting stuff (and some impenetrable stuff) in this Wikipedia article.

Health warning/conclusion

Most of the examples I’ve used here shouldn’t be used in production code unless there’s a very good reason. The common for(var i = 0; i < len; i++) is well known and easy to debug. If I was faced with having to debug for(;;foo[i++]) before doing the research for this article I would have been in difficulties, so be nice to the developers who have to maintain your code.

Nonetheless the for loop is an interesting control, and is specced in a way that is less restricted than I had assumed. While most of what I learnt researching this article might never be practical, it helped me understand JavaScript as a language a little bit better, and that’s a good thing.

Acknowledgement

A huge thanks to Toby for reading, checking and pushing me to further knowledge on this topic.

Tags