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:
- Initialization expression for the loop. Generally used to declare a loop control variable (e.g.
let i = 0;
). - Expression evaluated at the start of every loop iteration, to determine to execute the block statement when
true
or finish the loop whenfalse
. Generally used with control variable & limit to determine amount of iterations (e.g.i < 10;
). - 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.