1.2 Loops — Repeating Work in Code

In this section, you will learn to:
  • See what a loop is, and why it beats copying code by hand.
  • Write a for loop — init, condition, update.
  • Write a while loop, and know when to reach for each.
  • Loop over the items of a list.
  • Count and accumulate — a sum, a max, a tally.
  • Nest loops, and dodge the two classic pitfalls.

Say you want a program to print "hello" five times. The hard way is to write it out five times in a row:

console.log("hello");
console.log("hello");
console.log("hello");
console.log("hello");
console.log("hello");

Five lines. Now imagine a thousand. The loop way says it once and repeats it:

for (let i = 0; i < 5; i++) {
  console.log("hello");
}

Three lines instead of five — and turning this into a thousand repeats is a one-character edit, changing the 5. A loop is how we tell a computer to repeat an instruction as many times as we need, without writing it out more than once.

Definition 1.2.1: Loop

A loop is an instruction that runs the same block of code — its body — again and again, until a condition tells it to stop. The loop runs the body, checks whether to keep going, and if so runs it again. Each single pass through the body is one iteration.

Underneath, every loop is answering the same three small questions: where do I start, should I keep going, and what comes next? A for loop packs all three into one line; a while loop asks only the middle one and leaves the rest to you.

The for loop is the workhorse when you know roughly how many passes you need. It counts with a variable — usually i — that it initializes once, tests before every pass, and updates after every pass:

for (let i = 0; i < 5; i++) {
  console.log(i);
}

Reading it left to right: let i = 0 is the init, run once before the first pass; i < 5 is the condition, checked before every pass; and i++ is the update, run after every pass. This one prints 0, 1, 2, 3, 4 — five iterations, not six, because the condition fails as soon as i reaches 5.

Tracing a loop by hand — writing down what the counter is, whether the condition holds, and what prints — is the fastest way to catch a mistake before you run anything. Read across each row: check the condition, run the body, then update.

Table 1.2.1 — tracing for (let i = 1; i <= 5; i++) pass by pass.
pass\(i\)\(i \le 5\)?prints
11yes1
22yes2
33yes3
44yes4
55yes5
66no— stop

A while loop drops the counter and just repeats while its condition holds — no counter required. Use it when the number of passes depends on what happens inside the loop, like "keep doubling until we pass 100":

let n = 1;
while (n <= 100) {
  n = n * 2;
}
console.log(n);   // 128

You may loop zero times, or many; nothing about a while loop promises a fixed count. That gives a simple rule for picking between the two forms. Reach for for when you know how many times, or you're stepping a counter over a range or a list — the count is built into the loop header. Reach for while when you'll stop on a condition, not a count — "until the user quits," "until the number is small enough."

Two keywords change a loop's flow mid-pass. break leaves the loop immediately, skipping every remaining iteration:

for (let i = 1; i <= 10; i++) {
  if (i === 5) break;
  console.log(i);   // 1 2 3 4
}

continue is gentler — it skips only the rest of the current pass and moves on to the next one:

for (let i = 1; i <= 5; i++) {
  if (i % 2 === 0) continue;
  console.log(i);   // 1 3 5
}

The most common job a loop does is visit every item in a list. The counter doubles as the item's position, or index:

let fruits = ["apple", "pear", "plum"];
for (let i = 0; i < fruits.length; i++) {
  console.log(i + ": " + fruits[i]);
}
// 0: apple
// 1: pear
// 2: plum

Or, shorter, when you don't need the index at all: for (let f of fruits) { … } walks each item directly.

Try It Now 1 — what does this print?

A loop counts down from 3 while it stays above 0, then a line after the loop prints "go!". Trace it in your head first, then run it to check.

Editor
runs in your browser
▶ Press Run to see the output…
Solution

The counter starts at 3 and steps down by one each pass, so it prints 3, 2, 1, go! — a countdown. When i hits 0 the condition i > 0 is false, the loop stops, and only then does the last line run.

The pattern behind almost every useful loop is to keep a variable outside the loop and grow it on every pass. Start it at a sensible value — zero for a sum, the first item for a maximum, zero for a tally — and let the body do the work.

Worked Example 1.2.1

Add up a list of numbers with a loop.

Editor
runs in your browser
▶ Press Run to see the output…
Solution

Start an accumulator, total, at \(0\) outside the loop so it survives every pass, then add each item as the loop visits it:

let nums = [4, 8, 15, 16, 23, 42];
let total = 0;
for (let x of nums) {
  total += x;
}
console.log(total);   // 108

total lives outside the loop so it survives every pass; the body just grows it. Sum, product, max, count — all follow this same shape.

Worked Example 1.2.2

Find the largest value in a list with a loop.

Editor
runs in your browser
▶ Press Run to see the output…
Solution

Guess the first item is biggest, then let the loop correct you:

let nums = [4, 8, 15, 16, 23, 42];
let max = nums[0];
for (let x of nums) {
  if (x > max) max = x;
}
console.log(max);   // 42

Each pass compares one item to the current champion and updates if it's bigger. After the last item, max holds the winner.

Worked Example 1.2.3

Count how many items in a list are even.

Editor
runs in your browser
▶ Press Run to see the output…
Solution

A tally is a sum that only counts the hits — add an if inside the loop to decide which ones count:

let nums = [4, 8, 15, 16, 23, 42];
let evens = 0;
for (let x of nums) {
  if (x % 2 === 0) evens++;
}
console.log(evens);   // 4

The loop visits every item; the if decides which ones count. Here \(4, 8, 16, 42\) are even — four of them.

A loop needs no formula to add up \(1 + 2 + \cdots + 100\) — it just adds one number at a time, in exactly \(100\) steps, and lands on

$$ 1 + 2 + \cdots + 100 = \frac{100 \cdot 101}{2} = 5050. $$

That closed form is a nice check that the loop and the arithmetic agree.

The body runs exactly \(n\) times

A loop trades code length for repetition: three lines can stand in for a thousand.

A loop can sit inside another loop. For each pass of the outer loop, the inner loop runs start to finish — three outer passes times three inner passes is nine lines:

for (let r = 1; r <= 3; r++) {
  for (let c = 1; c <= 3; c++) {
    console.log(r, c);
  }
}

Nested loops are how we handle grids, tables, and pairs. Building each row as a string before printing it makes a multiplication grid:

for (let r = 1; r <= 3; r++) {
  let row = "";
  for (let c = 1; c <= 3; c++) {
    row += (r * c) + " ";
  }
  console.log(row);
}
// 1 2 3
// 2 4 6
// 3 6 9

Here the outer loop is the row and the inner loop is the columns across that row. The inner count can also depend on the outer counter, not just repeat a fixed number of times — one more star each row prints a triangle:

for (let r = 1; r <= 4; r++) {
  console.log("*".repeat(r));
}
// *
// **
// ***
// ****

Row r prints r stars — the shape grows with the counter.

Try It Now 2 — how many stars in total?

The triangle above runs rows \(1\) through \(4\), printing \(r\) stars on row \(r\). How many stars does it print in total? The version below also tallies a running total as it goes — run it to check.

Editor
runs in your browser
▶ Press Run to see the output…
Solution

Row by row that's \(1 + 2 + 3 + 4 = 10\) stars. Extend the same triangle to ten rows and you'd print \(1 + 2 + \cdots + 10 = 55\) — the same running-sum pattern as Worked Example 1.2.1, just counted in stars instead of numbers.

Two mistakes account for most loop bugs. The first is an off-by-one error — running the body one time too many:

let a = [10, 20, 30];
for (let i = 0; i <= a.length; i++) {
  console.log(a[i]);   // ...30, undefined
}

Indexes are \(0, 1, 2\) — but i <= 3 also tries a[3], which doesn't exist. The fix is to stop before the length, not at it:

let a = [10, 20, 30];
for (let i = 0; i < a.length; i++) {
  console.log(a[i]);   // 10, 20, 30
}

Use <, not <=, when looping to length. The second mistake is the infinite loop — a condition that never turns false because nothing inside the loop moves it:

let i = 0;
while (i < 5) {
  console.log(i);
}   // i stays 0 — forever

The fix is to make progress toward the exit on every pass:

let i = 0;
while (i < 5) {
  console.log(i);
  i++;   // now it reaches 5 and stops
}
Give every loop a way to stop

Every loop needs a reason to stop: a counter that advances, or a condition that eventually turns false. Forget it and the program hangs.

Try It Now 3 — spot the bug

This loop looks like it should sum \(1\) through \(10\). Does it? Run it and see.

Editor
runs in your browser
▶ Press Run to see the output…
Solution

Read the condition again: i < 10 never lets i reach \(10\), so the loop stops at \(9\) and prints \(45\), not \(55\). Fix it with i <= 10. The same sum-1-to-\(n\) shape from Worked Example 1.2.1 reappears here, and the same off-by-one habit — check whether the boundary value should be included — catches it.

The fastest way to trust a loop is to run it. The example below is live: the same sum-1-to-\(n\) accumulator from Worked Example 1.2.1, followed by the same triangle from the nested loops above. Change a bound, print a bigger triangle, sum only the odd numbers — then press Run.

Try it live — edit the code and run it, right here
runs in your browser — edit freely, then Run
▶ Press Run to see the output…

In one breath:

  1. A loop runs a body again and again until a condition stops it.
  2. for when you know the count or step a counter; while when you stop on a condition.
  3. Keep an accumulator outside the loop to sum, find a max, or tally.
  4. Nest loops for grids and patterns; the inner loop runs fully each outer pass.
  5. Watch for off-by-one and the infinite loop — always give the loop a way to stop.

A loop trades repetition for a tiny bit of bookkeeping — a counter or a condition. Once you can read the three parts of a loop, most real programs stop looking mysterious. The fastest way to trust a loop is to run it: change a bound, print a bigger triangle, sum only the odd numbers.

You can now write for and while loops, loop over lists, accumulate results, nest loops, and avoid the classic bugs. Next: §1.3 Functions — packaging code you can reuse.