Skip to content
aviral gupta

// B3.3 · ~30 min · Beginner

for, while and for...of

After this lesson you repeat code with a counting for loop, while and do...while, walk through arrays and strings with for...of, and write loops that stop, with no step too many.

Lesson 3 of 5 in B3 Control flow and loops

You will be able to

  • Write a counting for loop and a while or do...while loop, and trace how often each runs
  • Walk through arrays and strings with for...of, and know that for...in is not for arrays
  • Spot off-by-one errors and loops that never end, and make every loop stop
  1. Warm-up · Activity 1 of 7

    Warm-up: which loop always runs its block at least once, even when its condition is false from the start?

  2. Predict · Activity 2 of 7

    Predict before you read on: what does this print?

    const colors = ["red", "green", "blue"];
    for (let i = 0; i < colors.length; i++) {
      console.log(i, colors[i]);
    }
  3. Practice · Activity 3 of 7

    Fill in the keyword so that the loop prints the letters a, b and c, one per line.

    for (const letter ____ "abc") {
      console.log(letter);
    }
    for (const letter "abc") {
  4. Practice · Activity 4 of 7

    Trace the loop: what does this print?

    let total = 1;
    let steps = 0;
    while (total < 20) {
      total = total * 2;
      steps++;
    }
    console.log(total, steps);
  5. Practice · Activity 5 of 7

    Each loop runs three times. Match each loop header to the values i takes.

  6. Brain teaser · Activity 6 of 7

    Brain teaser. The condition n < 5 is false from the start. What does this print?

    let n = 10;
    do {
      console.log(n);
      n++;
    } while (n < 5);
    console.log("end", n);
  7. Apply · Activity 7 of 7

    Mini-task. Write a rocket countdown: a for loop that prints 5, 4, 3, 2 and 1, one per line, then Liftoff!. Then use for...of over the string "go" to print it in capitals with a space after each letter, trimmed at the end: G O. Before you run it, point to the line in each loop that brings it closer to its end.

    Check your work against this list

Build it yourself

Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.

Worked example

A shopping receipt

This program works through a basket with each kind of loop. for...of adds up the prices, a counting for loop numbers the items, a while loop works out how many weeks of saving 3 EUR it takes to pay, and for...of over a string stamps the receipt. Run it, then add a price to the list or change the savings per week, and predict the lines before you run it again.

main.js

// Work through a basket with three kinds of loop.
const prices = [2.5, 4, 1.25, 3];
const word = "paid";

let total = 0;
for (const price of prices) {
  total += price;
}
console.log("Items:", prices.length, "Total:", total);

for (let i = 0; i < prices.length; i++) {
  console.log(`Item ${i + 1}: ${prices[i]} EUR`);
}

let saved = 0;
let weeks = 0;
while (saved < total) {
  saved += 3;
  weeks++;
}
console.log("Saving 3 EUR a week takes", weeks, "weeks");

let stamp = "";
for (const letter of word) {
  stamp += letter.toUpperCase() + ".";
}
console.log(stamp);

Run it with

node main.js

Output

Items: 4 Total: 10.75
Item 1: 2.5 EUR
Item 2: 4 EUR
Item 3: 1.25 EUR
Item 4: 3 EUR
Saving 3 EUR a week takes 4 weeks
P.A.I.D.
  • for...of hands over each price in turn; total += price adds it to the running total.
  • i starts at 0 because positions do, so the item number is i + 1.
  • The while loop stops as soon as saved, 12, is no longer below 10.75: four weeks.
  • for...of over a string gives one character per pass.
Change it and run it

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Exercises

Exercise 1 of 2

Welcome every guest

The program should greet each guest on the list and then print the number of guests: Welcome, Ada!, Welcome, Grace!, Welcome, Linus! and 3 guests. It skips Ada and greets someone called undefined instead. Fix the start and the condition of the for loop, and keep the list as it is.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Hints
  1. Hint 1

    Positions in an array start at 0. Which position does the loop start at?

  2. Hint 2

    guests.length is 3, but the last position is 2. What does guests[3] give?

  3. Hint 3

    Start at let i = 0 and loop while i < guests.length.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

const guests = ["Ada", "Grace", "Linus"];

for (let i = 0; i < guests.length; i++) {
  console.log(`Welcome, ${guests[i]}!`);
}
console.log(`${guests.length} guests`);
Run it on your computer

Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.js

const guests = ["Ada", "Grace", "Linus"];

for (let i = 1; i <= guests.length; i++) {
  console.log(`Welcome, ${guests[i]}!`);
}
console.log(`${guests.length} guests`);

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {runMain} from './learnrun.js';

const lines = async () => (await runMain()).trimEnd().split('\n');

test('greets Ada first', async () => {
  const got = await lines();
  assert.equal(got[0], 'Welcome, Ada!', `the first line is ${JSON.stringify(got[0])}, expected "Welcome, Ada!"`);
});

test('greets no one called undefined', async () => {
  const got = await lines();
  assert.ok(!got.includes('Welcome, undefined!'), `the program printed ${JSON.stringify(got)}`);
});

test('greets all three guests, then counts them', async () => {
  const got = await lines();
  assert.deepEqual(got, ['Welcome, Ada!', 'Welcome, Grace!', 'Welcome, Linus!', '3 guests'], `the program printed ${JSON.stringify(got)}`);
});

package.json

{
  "type": "module"
}

package.json tells Node.js that the .js files are modules; keep it in the folder.

Run the program:

node main.js

Run the checks (needs learnrun.js in the same folder):

node --test
Download learnrun.js

Exercise 2 of 2

Count and halve

Finish two loops. First, use for...of to count how many times the letter a appears in word, so the program prints a appears 3 times. Then use a while loop that halves value (divides it by 2) until it is below 1, counting each halving, so the program prints 7 halvings. Make sure your while loop changes value on every pass.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Hints
  1. Hint 1

    for (const letter of word) gives b, a, n, a, n, a. Inside, an if can test letter === "a" and add 1 to count.

  2. Hint 2

    The while condition says when to keep going: while (value >= 1).

  3. Hint 3

    Inside the while block, value = value / 2; and halvings++; bring the loop closer to its end on every pass.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

const word = "banana";
let count = 0;
for (const letter of word) {
  if (letter === "a") {
    count++;
  }
}
console.log(`a appears ${count} times`);

let value = 100;
let halvings = 0;
while (value >= 1) {
  value = value / 2;
  halvings++;
}
console.log(`${halvings} halvings`);
Run it on your computer

Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.js

const word = "banana";
let count = 0;
// Count each "a" in word with for...of.

console.log(`a appears ${count} times`);

let value = 100;
let halvings = 0;
// Halve value with a while loop until it is below 1, counting the halvings.

console.log(`${halvings} halvings`);

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {runMain} from './learnrun.js';

const lines = async () => (await runMain()).trimEnd().split('\n');

test('counts the letter a in banana', async () => {
  const got = await lines();
  assert.equal(got[0], 'a appears 3 times', `the first line is ${JSON.stringify(got[0])}, expected "a appears 3 times"`);
});

test('halves 100 until it is below 1', async () => {
  const got = await lines();
  assert.equal(got[1], '7 halvings', `the second line is ${JSON.stringify(got[1])}, expected "7 halvings"`);
});

test('prints exactly two lines', async () => {
  const got = await lines();
  assert.equal(got.length, 2, `the program printed ${got.length} line(s), expected 2`);
});

package.json

{
  "type": "module"
}

package.json tells Node.js that the .js files are modules; keep it in the folder.

Run the program:

node main.js

Run the checks (needs learnrun.js in the same folder):

node --test
Download learnrun.js

Common mistakes

const as the counter of a for loop

for (const i = 0; i < 3; i++) {
  console.log(i);
}

What Node.js prints

TypeError: Assignment to constant variable.

Why, and the fix

The first pass works and prints 0, but then i++ tries to give the constant i a new value, and the program stops. A counting for loop changes its counter, so declare it with let: for (let i = 0; i < 3; i++). const is right in for...of, where each pass gets a new variable.

One pass too many

const items = ["tea", "milk", "bread"];
for (let i = 0; i <= items.length; i++) {
  console.log(items[i].toUpperCase());
}

What Node.js prints

TypeError: Cannot read properties of undefined (reading 'toUpperCase')

Why, and the fix

The three items print, then the loop runs once more with i = 3. items[3] does not exist, so it is undefined, and undefined has no toUpperCase. The last position is length - 1, so the condition must be i < items.length. Or use for...of, which has no positions to get wrong.

for...of over a number

for (const n of 3) {
  console.log(n);
}

What Node.js prints

TypeError: 3 is not iterable

Why, and the fix

for...of needs something with values to walk through, such as an array or a string. A number has none, so it is not iterable. To repeat something three times, use a counting loop: for (let n = 0; n < 3; n++).

JavaScript in the browser: your browser’s own engine, in a sandboxed worker. Syntax errors are located with acorn 8.18.0, MIT. Licence and source

Exit ticket

5 questions, no hints. Score 80% or more to complete the lesson.

Finish every activity above to unlock the exit ticket.

Report a problem

Spotted something wrong or unclear? Say what, and it will be checked and fixed.

#

At least 20 characters.

Only if you want a reply.

Key ideas

for counts, while waits

for (let i = 0; i < 3; i++) { … } has three parts. The start, let i = 0, runs once. The condition, i < 3, is checked before every pass. The update, i++, adds 1 to i after every pass. So the block runs for i = 0, 1 and 2: three times. while (condition) { … } has only the condition: use it when you do not know in advance how many passes you need, such as saving until a price is reached. do { … } while (condition); checks after the block, so its block always runs at least once.

for...of walks through the values

for (const item of list) { … } gives you each item of an array in turn, with no counter to get wrong. It works on strings too, one character at a time: for (const ch of "hey") gives h, e and y. const is right here, because each pass gets a fresh variable. Use a counting for loop when you also need the position, i. There is also for...in, which walks through the names of an object’s properties. It is not meant for arrays: it gives the positions as strings, such as "0", and can include extra properties.

Off by one, and loops that never stop

An array of 3 items has the positions 0, 1 and 2, and its length is 3. So a loop over positions starts at 0 and runs while i < list.length. Write i <= list.length, and the last pass reads list[3], which is undefined: an off-by-one error. A loop stops only when its condition becomes false. If nothing in the block moves towards that, such as a while whose counter never changes, the loop runs forever, and the page or program freezes. Before you run a loop, find the line that changes what the condition tests.

Sources

Last reviewed October 1, 2026