Loops: while and for

We often have a need to perform similar actions many times in a row.

For example, when we need to output goods from the list one after another. Or just run the same code for each number from 1 to 10.

Loops are a way to repeat the same part of code multiple times.

The “while” loop

The while loop has the following syntax:

while (condition) {
  // code
  // so-called "loop body"
}

While the condition is true – the code from the loop body is executed.

For instance, the loop below outputs i while i<3:

let i = 0;
while (i < 3) { // shows 0, then 1, then 2
  alert( i );
  i++;
}

A single execution of the loop body is called an iteration. The loop in the example above makes 3 iterations.

If there were no i++ in the example above, the loop would repeat (in theory) forever. In practice, the browser provides ways to stop such loops, and for server-side JavaScript we can kill the process.

Any expression or a variable can be a loop condition, not just a comparison. They are evaluated and converted to boolean by while.

For instance, the shorter way to write while (i!=0) could be while (i):

let i = 3;
while (i) { // when i becomes 0, the condition becomes falsy, and the loop stops
  alert( i );
  i--;
}
Brackes are not required for a single-line body

If the loop body has a single statement, we can omit the brackets {…}:

let i = 3;
while (i) alert(i--);

The “do…while” loop

The condition check can be moved below the loop body using the do..while syntax:

do {
  // loop body
} while (condition);

The loop will first execute the body, then check the condition, and while it’s truthy – execute it again and again.

For example:

let i = 0;
do {
  alert( i );
  i++;
} while (i < 3);

This form of syntax is rarely used. Usually, if there’s no special reason, the other form is preferred: while(…) {…}.

The “for” loop

The for loop is the most often used one.

It looks like this:

for (begin; condition; step) {
  // ... loop body ...
}

Let’s learn the meaning of these parts by example. The loop below runs alert(i) for i from 0 up to (but not including) 3:

for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2
  alert(i);
}

Let’s examine the for statement part by part:

part
begin i=0 Executes once upon entering the loop.
condition i<3 Checked before every loop iteration, if fails the loop stops.
step i++ Executes after the body on each iteration, but before the condition check.
body alert(i) Runs again and again while the condition is truthy

The general loop algorithm works like this:

Run begin
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ ...

If you are new to loops, then maybe it would help if you go back to the example and reproduce how it runs step-by-step on a piece of paper.

Here’s what exactly happens in our case:

// for (let i = 0; i < 3; i++) alert(i)

// run begin
let i = 0
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// ...finish, because now i == 3
Inline variable declaration

Here the “counter” variable i is declared right in the loop. That’s called an “inline” variable declaration. Such variable is visible only inside the loop.

for (let i = 0; i < 3; i++) {
  alert(i); // 0, 1, 2
}
alert(i); // error, no such variable

Instead of defining a variable, we can use an existing one:

let i = 0;

for (i = 0; i < 3; i++) { // use an existing variable
  alert(i); // 0, 1, 2
}

alert(i); // 3, visible, because declared outside of the loop

Skipping parts

Any part of for can be skipped.

For example, we can omit begin if we don’t need to do anything at the loop start.

Like here:

let i = 0; // we have i already declared and assigned

for (; i < 3; i++) { // no need for "begin"
  alert( i ); // 0, 1, 2
}

We can also remove the step part:

let i = 0;

for (; i < 3;) {
  alert( i );
}

The loop became identical to while (i<3).

We can actually remove everything, thus creating an infinite loop:

for (;;) {
  // repeats without limits
}

Please note that the two for semicolons ; must present, otherwise it would be a syntax error.

Breaking the loop

Normally the loop exits when the condition becomes falsy.

But we can force the exit at any moment. There’s a special break directive for that.

For example, this code below asks user for numbers and breaks if no number entered:

let sum = 0;

while (true) {

  let value = +prompt("Enter a number", '');

  if (!value) break; // (*)

  sum += value;

}
alert( 'Sum: ' + sum );

The break directive is activated in the line (*) if the user enters an empty line or cancels the input. It stops the loop immediately, passing the control to the first line after the loop. Namely, alert.

The combination: “infinite loop + break as needed” is great for situations when the condition must be checked not in beginning/end of the loop, but in the middle. Or even in several places of the body.

Continue to the next iteration

The continue directive is a “lighter version” of break. It doesn’t stop the whole loop. Instead if stops the current iteration and forces the loop to start a new one (if the condition allows).

We can use it if we’re done on the current iteration and would like to move on to the next.

The loop above uses continue to output only odd values:

for (let i = 0; i < 10; i++) {

  // if true, skip the remaining part of the body
  if (i % 2 == 0) continue;

  alert(i); // 1, then 3, 5, 7, 9
}

For even values of i the continue directive stops body execution, passing the control to the next iteration of for (with the next number). So the alert is only called for odd values.

The directive continue helps to decrease nesting level

A loop that shows odd values could look like this:

for (let i = 0; i < 10; i++) {

  if (i % 2) {
    alert( i );
  }

}

From the technical point of view it’s identical to the example above. Surely, we can just wrap the code in the if block instead of continue.

But as a side-effect we got one more figure brackets nesting level. If the code inside if is longer than a few lines, that may decrease the overall readability.

No break/continue to the right side of ‘?’

Please note that syntax constructs that are not expressions cannot be used in '?'. In particular, directives break/continue are disallowed there.

For example, if we take this code:

if (i > 5) {
  alert(i);
} else {
  continue;
}

…And rewrite it using a question mark:

(i > 5) ? alert(i) : continue; // continue not allowed here

…Then it stops working. The code like this will give a syntax error:

That’s just another reason not to use a question mark operator '?' instead of if.

Labels for break/continue

Sometimes we need to break out from multiple nested loops at once.

For example, in the code below we loop over i and j prompting for coordinates (i, j) from (0,0) to (3,3):

for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`Value at coords (${i},${j})`, '');

    // what if I want to exit from here to Done (below)?

  }
}

alert('Done!');

We need a way to stop the process if the user cancels the input.

The ordinary break after input would only break the inner loop. That’s not sufficient. Labels come to the rescue.

A label is an identifier with a colon before a loop:

labelName: for(...) {
  ...
}

The break <labelName> statement in the loop breaks out to the label.

Like here:

outer: for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`Value at coords (${i},${j})`, '');

    // if an empty string or canceled, then break out of both loops
    if (!input) break outer; // (*)

    // do something with the value...
  }
}
alert('Done!');

In the code above break outer looks upwards for the label named outer and breaks out of that loop.

So the control goes straight from (*) to alert('Done!').

We can also move a label into the separate string:

outer:
for (let i = 0; i < 3; i++) { ... }

The continue directive can also be used with a label. In this case the execution jumps to the next iteration of the labelled loop.

Labels are not a “goto”

Labels do not allow to jump into an arbitrary place of code.

For example, it is impossible to do like this:

break label;  // jumps to label? No.

label: for(...)

The call to a break/continue is only possible from inside the loop, and the label must be somewhere upwards from the directive.

Summary

We covered 3 types of loops:

  • while – the condition is checked before each iteration.
  • do..while – the condition is checked after each iteration.
  • for(;;) – the condition is checked before each iteration, additional settings available.

To make an “infinite” loop, usually the while(true) construct is used. Such a loop, just like any other, can be stopped with the break directive.

If we don’t want to do anything on the current iteration and would like to forward to the next one – the continue directive does it.

Break/continue support labels before the loop. A label is the only way for break/continue to escape the nesting and go to the outer loop.

Tasks

importance: 3

What is the last value alerted by this code? Why?

let i = 3;

while (i) {
  alert( i-- );
}

The answer: 1.

let i = 3;

while (i) {
  alert( i-- );
}

Every loop iteration decreases i by 1. The check while(i) stops the loop when i = 0.

Hence, the steps of the loop form the following sequence (“loop unrolled”):

let i = 3;

alert(i--); // shows 3, decreases i to 2

alert(i--) // shows 2, decreases i to 1

alert(i--) // shows 1, decreases i to 0

// done, while(i) check stops the loop
importance: 4

For every loop, write down which values it shows, in your opinion. And then compare with the answer.

Both loops alert same values or not?

  1. The prefix form ++i:

    let i = 0;
    while (++i < 5) alert( i );
  2. The postfix form i++

    let i = 0;
    while (i++ < 5) alert( i );

The task demonstrates how postfix/prefix forms can lead to different results when used in comparisons.

  1. From 1 to 4

    let i = 0;
    while (++i < 5) alert( i );

    The first value is i=1, because ++i first increments i and then returns the new value. So the first comparison is 1 < 5 and the alert shows 1.

    Then follow 2,3,4… – the values show up one after another. The comparison always uses the incremented value, because ++ is before the variable.

    Finally, i=4 is incremented to 5, the comparison while(5 < 5) fails, and the loop stops. So 5 is not shown.

  2. From 1 to 5

    let i = 0;
    while (i++ < 5) alert( i );

    The first value is again i=1. The postfix form of i++ increments i and then returns the old value, so the comparison i++ < 5 will use i=0 (contrary to ++i < 5).

    But the alert call is separate. It’s another statement which executes after the increment and the comparison. So it gets the current i=1.

    Then follow 2,3,4…

    Let’s stop on i=4. The prefix form ++i would increment it and use 5 in the comparison. But here we have the postfix form i++. So it increments i to 5, but returns the old value. Hence the comparison is actually while(4 < 5) – true, and the control goes on to alert.

    The value i=5 is the last one, because on the next step while(5 < 5) is false.

importance: 4

For each loop write down which values it is going to show. Then compare with the answer.

Both loops alert same values or not?

  1. The postfix form:

    for (let i = 0; i < 5; i++) alert( i );
  2. The prefix form:

    for (let i = 0; i < 5; ++i) alert( i );

The answer: from 0 to 4 in both cases.

for (let i = 0; i < 5; ++i) alert( i );

for (let i = 0; i < 5; i++) alert( i );

That can be easily deducted from the algorithm of for:

  1. Execute once i=0 before everything (begin).
  2. Check the condition i<5
  3. If true – execute the loop body alert(i), and then i++

The increment i++ is separated from the condition check (2). That’s just another statement.

The value returned by the increment is not used here, so there’s no difference between i++ and ++i.

importance: 5

Use the for loop to output even numbers from 2 to 10.

Run the demo

for (let i = 2; i <= 10; i++) {
  if (i % 2 == 0) {
    alert( i );
  }
}

We use the “modulo” operator % to get the remainder and check for the evenness here.

importance: 5

Rewrite the code changing the for loop to while without altering it’s behavior (the output should stay same).

for (let i = 0; i < 3; i++) {
  alert( `number ${i}!` );
}
let i = 0;
while (i < 3) {
  alert( `number ${i}!` );
  i++;
}
importance: 5

Write a loop which prompts for a number greater than 100. If the visitor enters another number – ask him to input again.

The loop must ask for a number until either the visitor enters a number greater than 100 or cancels the input/enters an empty line.

Here we can assume that the visitor only inputs numbers. There’s no need to implement a special handling for a non-numeric input in this task.

Run the demo

let num;

do {
  num = prompt("Enter a number greater than 100?", 0);
} while (num <= 100 && num);

The loop do..while repeats while both checks are truthy:

  1. The check for num <= 100 – that is, the entered value is still not greater than 100.
  2. The check && num is false when num is null or a empty strig. Then the while loop stops too.

P.S. If num is null then num <= 100 is false, so without the 2nd check the loop wouldn’t stop if the user clicks CANCEL. Both checks are required.

importance: 3

An integer number greater than 1 is called a prime if it cannot be divided without a remainder by anything except 1 and itself.

In other words, n>1 is a prime if it can’t be evenly divided by anything except 1 and n.

For example, 5 is a prime, because it cannot be divided without a remainder by 2, 3 and 4.

Write the code which outputs prime numbers in the interval from 2 to n.

For n=10 the result will be 2,3,5,7.

P.S. The code should work for any n, not be hard-tuned for any fixed value.

There are many algorithms for this task.

Let’s use a nested loop:

For each i in the interval {
  check if i has a divisor from 1..i
  if yes => the value is not a prime
  if no => the value is a prime, show it
}

The code using a label:

let n = 10;

nextPrime:
for (let i = 2; i <= n; i++) { // for each i...

  for (let j = 2; j < i; j++) { // look for a divisor..
    if (i % j == 0) continue nextPrime; // not a prime, go next i
  }

  alert( i ); // a prime
}

There’s a lot of space to opimize it. For instance, we could look for the divisors from 2 to square root of i. But anyway, if we want to be really efficient for large intervals, we need change the approach and rely on advanced maths and complex algorithms like Quadratic sieve, General number field sieve etc.

Tutorial map

Comments

read this before commenting…
  • You're welcome to post additions, questions to the articles and answers to them.
  • To insert a few words of code, use the <code> tag, for several lines – use <pre>, for more than 10 lines – use a sandbox (plnkr, JSBin, codepen…)
  • If you can't understand something in the article – please elaborate.