FullStack Course LogoFullStack Course
Module: JavaScript
JavaScript·048·4 MIN READ

048: Loops and Iteration

TOPICS COVERED: Loops and Iteration

Outcomes

By the end of this lesson, you can:

  • explain when repeated work needs iteration;
  • trace initialization, condition, body, and update in a for loop;
  • use a while loop when repetition depends on a changing condition;
  • process array values and accumulate a summary;
  • use break and continue deliberately; and
  • recognize and fix off-by-one and infinite-loop errors.

Prerequisites and Retrieval

Start by retrieving three ideas: conditions, arrays as ordered values, and reassignment.

  1. What does count < 5 produce?
  2. Why must an accumulating total use let?
  3. Which branch runs first in an else if chain?

The array method lessons come later. For now, process lists with explicit loops so you can see exactly what happens on every pass.

Terms

  • loop/iteration: Repeating statements until a condition ends the cycle. — Source: MDN: Looping code
  • iteration: One pass of the loop body. — Source: MDN: Loops and iteration
  • counter: Variable tracking loop progress, typically incremented each pass. — Source: MDN: Loops and iteration
  • initializer: Starting expression setting the counter before iteration begins. — Source: MDN: for
  • condition: Per-pass test deciding whether the body runs again. — Source: MDN: for
  • update: After-expression adjusting state at end of each pass. — Source: MDN: for
  • accumulator: Variable collecting a running total/result across iterations. — Source: MDN: Loops and iteration
  • break: Exits the nearest enclosing loop immediately. — Source: MDN: break
  • continue: Skips to the next iteration without leaving the loop. — Source: MDN: continue
  • nested loop: A loop inside another loop’s body. — Source: MDN: Loops and iteration
  • off-by-one error: Boundary mistake producing one extra/fewer pass (< vs <=, index math). — Source: MDN: Loops and iteration
  • infinite loop: Loop whose exit condition never becomes false. — Source: MDN: Loops and iteration
  • Iteration (official): "Iteration is repeating a set of instructions until a condition is met." — Source: MDN: Iteration protocols
  • Loop invariant: "A condition that must hold before and after each iteration." — Source: ECMAScript: Iteration Statements

Beginner Explanation and Mental Model

When several statements differ only by a value or index, copying them by hand creates code that is tedious to maintain and easy to get out of sync. A loop is a controlled replay button: you define where repetition starts, when it may continue, what happens during a pass, and how progress changes afterward.

A for loop keeps those controls together:

js
for (let index = 0; index < 3; index += 1) {
  console.log(index);
}

Trace the execution in this order:

  1. let index = 0 runs once.
  2. index < 3 is checked. If false, the loop ends.
  3. The body runs.
  4. index += 1 updates progress.
  5. Steps 2-4 repeat.

The loop logs 0, 1, and 2. Once index becomes 3, the condition is false and the body is not entered again. This boundary also matches zero-based array indexes: a list of length 3 has valid indexes 0 through 2, which is why index < list.length is the standard form.

A trace table is the safest beginner debugging tool for this code. Make columns for the counter, the condition result, the current item, and the accumulator after the body runs. Include a final row in which the condition becomes false; that row shows why execution stops. If you expect five passes, write down all five counter values before running the code. This exposes a wrong starting value or comparison without relying on trial and error.

A while loop puts only the continuation condition in its header:

js
let attempts = 0;
while (attempts < 3) {
  console.log("Attempt", attempts + 1);
  attempts += 1;
}

Use for when initialization, update, and boundary make a clear counting sequence. Use while when the changing state determines whether another pass is appropriate and the number of repetitions is not the central idea. There must always be a credible path through a while loop that eventually makes its condition false. If that path is missing, the loop does not terminate.

Processing and accumulating

For a summary, create the accumulator before the loop and update it during each pass:

js
const values = [4, 7, 2];
let total = 0;

for (let index = 0; index < values.length; index += 1) {
  total += values[index];
}

The array is const because the array binding is not reassigned. The total is let because its value is rebound on every pass. values[index] selects the item associated with the current index. Keeping those roles separate makes the loop easier to read and debug.

break, continue, and nesting

break leaves the nearest enclosing loop as soon as no more work is needed. continue skips the rest of the current iteration and proceeds to the next one. Both are useful control-flow tools, but use them deliberately: a loop with many jumps becomes difficult to trace.

A nested loop is a loop inside another loop’s body. It is useful for a small grid or another two-dimensional structure. If the outer and inner loops each run 3 times, their shared body runs 9 times. The work multiplies quickly, so nesting needs a clear purpose and a realistic estimate of how many executions it creates.

Worked Example: Number Summary

This loop maintains three pieces of state while it visits each number: a running total, a count of even values, and the largest value seen so far.

js
const numbers = [12, 5, 8, 21, 4];
let total = 0;
let evenCount = 0;
let largest = numbers[0];

for (let index = 0; index < numbers.length; index += 1) {
  const currentNumber = numbers[index];
  total += currentNumber;

  if (currentNumber % 2 === 0) {
    evenCount += 1;
  }

  if (currentNumber > largest) {
    largest = currentNumber;
  }
}

const average = total / numbers.length;
console.log("Total:", total);
console.log("Even count:", evenCount);
console.log("Largest:", largest);
console.log("Average:", average);

Expected output:

text
Total: 50
Even count: 3
Largest: 21
Average: 10

Before each pass, write a trace row containing index, currentNumber, total, evenCount, and largest. Initializing largest from the first element also works when every value is negative; initializing it to 0 would fail for [-8, -3] because neither value is greater than zero. This example assumes a non-empty array. Handling an empty input belongs in validation logic before reading the first element or dividing by the array length.

Intermediate Example: Skip and Stop

Now combine two different control-flow decisions. Process readings, ignore negative readings as invalid, and stop as soon as the sentinel value 999 appears:

js
const readings = [14, -1, 18, 999, 22];
let validTotal = 0;
let validCount = 0;

for (let index = 0; index < readings.length; index += 1) {
  const reading = readings[index];

  if (reading === 999) {
    break;
  }

  if (reading < 0) {
    continue;
  }

  validTotal += reading;
  validCount += 1;
}

console.log("Valid count:", validCount);
console.log("Valid total:", validTotal);

Expected output:

text
Valid count: 2
Valid total: 32

The -1 reaches continue, so the accumulator updates are skipped for that pass. The 999 reaches break, so the loop exits and 22 is never visited. Put the stop condition before the skip condition when a sentinel could otherwise match a skip rule; the stop decision should take precedence.

A small nested-loop example makes the execution order concrete:

js
for (let row = 1; row <= 2; row += 1) {
  for (let column = 1; column <= 3; column += 1) {
    console.log(`Row ${row}, column ${column}`);
  }
}

This prints six coordinates. For each row, the inner loop completes all three columns before the outer loop advances to the next row.

Optional Advanced Extension: for...of

When the index itself is not useful, for...of expresses the intent more directly than manual array indexing:

js
const prices = [100, 250, 50];
let total = 0;

for (const price of prices) {
  total += price;
}

console.log(total); // 400

The price binding is created for each pass and is const because that binding is not reassigned during its pass. Do not use for...in to obtain array values. It enumerates property keys and therefore has different semantics.

Deep Dive: for, for...of, and for...in

Choose the loop according to the thing you need to iterate and the information you need from it.

js
for (let index = 0; index < items.length; index += 1) {
  console.log(index, items[index]);
}

Use a classic for when explicit index control matters, such as when you need the position as well as the value.

js
for (const item of items) {
  console.log(item);
}

Use for...of for iterable values such as arrays, strings, maps, and sets.

js
const stock = { biryani: 10, juice: 4 };

for (const key in stock) {
  if (Object.hasOwn(stock, key)) {
    console.log(key, stock[key]);
  }
}

Use for...in for enumerable property keys, typically on objects. It should not be your default array loop.

do...while

Sometimes the operation must happen once before its condition can be evaluated. do...while guarantees at least one execution:

js
let attempt = 0;

do {
  attempt += 1;
} while (attempt < 3);

Nested-loop escape with labels

Labels are available for leaving an outer loop from inside a nested loop, but they should be rare:

js
outer:
for (const row of grid) {
  for (const cell of row) {
    if (cell === target) {
      break outer;
    }
  }
}

In most cases, a helper function, some, or find communicates the search more clearly. Learn to read labels when you encounter them; do not force them into ordinary solutions.

Mistakes and Debugging

  • Infinite loop: the condition remains true because the update is missing, moves in the wrong direction, or changes a different variable.
  • Off-by-one: index <= items.length visits one invalid index. Use < items.length.
  • Starting array traversal at 1: index 0 is the first item.
  • Resetting an accumulator inside the loop: initialize it once before the loop.
  • Using const for an accumulator/counter: use let because reassignment is intentional.
  • Mutating loop bounds unexpectedly: avoid changing array length while traversing it in beginner code.
  • continue before a while update: this can skip progress and create an infinite loop. Update before continuing or choose a for loop.
  • Overusing nested loops: estimate body executions by multiplying iteration counts.

If a page freezes, stop script execution or close the tab. Then inspect the initializer, condition, and update on paper. Logging the counter with a small input can help, but never flood the console with output from an unbounded loop.

Test loops with an empty array, a one-element array, and a typical multi-element array. Empty input verifies that the body can safely run zero times. One element exposes an incorrect starting index. Multiple elements exercise updates and accumulation. For stop/skip logic, include cases where the sentinel is first, in the middle, last, and absent. These small, intentional test sets reveal control-flow errors more clearly than one large list.

Best Practices

  • Use let for counters and accumulators; use const for current values that are not rebound.
  • Use descriptive names such as index, total, and reading.
  • Traverse arrays with index < array.length.
  • Keep loop bodies small and move invariant calculations outside.
  • Validate empty arrays before calculations that require a first element or division by length.
  • Prefer for...of when the index is unnecessary.
  • Use break for a genuine stop and continue for a genuine skip, not as substitutes for clear structure.
  • Never intentionally create an infinite loop in browser practice.

Tiered Exercises

Core

Use a for loop to print numbers 1-10 and calculate their total. Then use a while loop to count down from 5 to 1.

Practice

Given [3, 10, 7, 12, 5], calculate total, count values greater than 6, and find the largest value using one loop.

Professional Extension

Given [4, -2, 9, -1, 0, 7], skip negatives, stop at zero, and total only values processed before the stop. Log each accepted value and final total.

Complete Solutions

js
let total = 0;
for (let number = 1; number <= 10; number += 1) {
  console.log(number);
  total += number;
}
console.log("Total:", total); // 55

let countdown = 5;
while (countdown >= 1) {
  console.log(countdown);
  countdown -= 1;
}
js
const values = [3, 10, 7, 12, 5];
let total = 0;
let greaterThanSix = 0;
let largest = values[0];

for (const value of values) {
  total += value;
  if (value > 6) {
    greaterThanSix += 1;
  }
  if (value > largest) {
    largest = value;
  }
}

console.log(total, greaterThanSix, largest); // 37 3 12
js
const values = [4, -2, 9, -1, 0, 7];
let total = 0;

for (const value of values) {
  if (value === 0) {
    break;
  }
  if (value < 0) {
    continue;
  }
  console.log("Accepted:", value);
  total += value;
}

console.log("Total:", total); // 13

Recap and Exit Questions

Loops repeat controlled work. A for loop groups setup, continuation, and update; a while loop puts the emphasis on a state condition. Accumulators summarize values, while break stops and continue skips. A reliable loop always makes measurable progress toward termination.

  1. Name the four moving parts of a for loop.
  2. Why is < array.length the normal array boundary?
  3. How do break and continue differ?
  4. What creates an infinite loop?
  5. When is for...of preferable?

Official References

References checked 2026-08-24.

Reader page: /javascript/lesson/048/loops-and-iteration