for statement in JS

Explicit counters, iterations, step sizes and performance considerations  

The earliest JavaScript for loop you're likely to encounter to this day is one with a for statement. The reason the for statement is still in use today, is because it's the most flexible, with the ability to prematurely finish a loop when a condition is true, define the number of times a loop runs by something other than a data structure's size, as well as adjust the increments in which iterations are made (a.k.a. step size).

The syntax of the for statement consists of three expressions separated by ; wrapped in () and prefixed with for, with the block of code to execute on each iteration wrapped in a block statement by curly brackets {}. The purpose of the three expressions that follow the for statement is the following:

  1. Initialization expression for the loop. Generally used to declare a loop control variable (e.g.let i = 0;).
  2. Expression evaluated at the start of every loop iteration, to determine to execute the block statement when true or finish the loop when false. Generally used with control variable & limit to determine amount of iterations (e.g. i < 10;).
  3. Expression evaluated at the end of every loop iteration. Generally used to increase or decrease loop control variable (e.g.i++;)

Due to the flexibility of the for statement, strictly speaking all three expressions that accompany a for statement are optional parameters. However, although it's valid to declare a for loop in the form for(;;) { } -- which is an endless loop -- and rely on a statement inside the block statement to terminate it, this type of for syntax without expressions can be non-obvious and confusing, not to mention there are other more obvious JavaScript for loop syntax alternatives to declare endless loops, such as the the while and do while statements.

Although the expressions to control a for statement can appear to be fail-safe, the manner in which these expressions are declared can have an impact on performance. This impact varies when declaring the control variable with the var keyword or the let keyword -- because it influences block scope visibility and hoisting -- as well as if the number of iterations are specified as a constant or calculated on every iteration.

Listing 6-1 illustrates four variations of this classic JavaScript for loop syntax with performance metrics using the console object's console.time() & console.timeEnd() methods.

Listing 6-1. For loops with for statements and performance metrics
let primeNumbers = [2,3,5,7,11];

// Option 1) Least efficient
// var as counter & size in loop definition
console.time("loopOption1");
for (var i = 0; i < primeNumbers.length; i++) {
  console.log(primeNumbers[i]);
}
console.timeEnd("loopOption1");

// Option 2) More efficient
// var predefined with counter & size
var primeLength = primeNumbers.length;

console.time("loopOption2");
for (var j = 0; j < primeLength; j++) {
  console.log(primeNumbers[j]);
}
console.timeEnd("loopOption2");

// Option 3) More efficient
// let as count & size in loop definition
console.time("loopOption3");
for (let k = 0; k < primeNumbers.length; k++) {
  console.log(primeNumbers[k]);
}
console.timeEnd("loopOption3");

// Option 4) Most efficient
// let as count & size in predefined const
const primeLengthConst = primeNumbers.length;

console.time("loopOption4");
for (let l = 0; l < primeLengthConst; l++) {
  console.log(primeNumbers[l]);
}
console.timeEnd("loopOption4");

The first option and least efficient syntax presented in listing 6-1 consists of declaring the control variable with the var keyword and declaring the number of times the loop runs directly with the .length property of the data structure. The problem with this first option is due to block scope & hoisting it leads to the repeated calculation of a constant value. In this case, the i for loop control variable is declared inline, which forces the JavaScript engine to hoist the variable. In addition, an evaluation is made against primeNumbers.length on every iteration to determine when to finish the loop, something that's inefficient for a value that doesn't change over the course of a loop.

The second option in listing 6-1 produces a slightly more efficient for loop by declaring the number of iterations in a predefined variable primeLength vs. calling the .length property on the data structure on every iteration. In the third option, the loop is executed even more efficiently by declaring the counter variable with the let keyword -- to avoid hoisting -- although it still uses the .length property on the data structure on every iteration.

The fourth option in listing 6-1 which produces the most efficient for loop, uses the let keyword to declare the control variable and declares the number of times the loop runs in a predefined variable primeLengthConst as a const vs. calling the .length property on the data structure on every iteration. As you can see from these examples, the biggest performance impact in this kind of for loop is in the control variable delivering more performance with the let keyword vs. var, followed by declaring the number of iterations in a predefined variable with const vs. calculating the number of iterations every time with the .length property of a data structure.