048: 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
forloop; - use a
whileloop when repetition depends on a changing condition; - process array values and accumulate a summary;
- use
breakandcontinuedeliberately; 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.
- What does
count < 5produce? - Why must an accumulating total use
let? - Which branch runs first in an
else ifchain?
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: breakcontinue: 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:
for (let index = 0; index < 3; index += 1) {
console.log(index);
}
Trace the execution in this order:
let index = 0runs once.index < 3is checked. If false, the loop ends.- The body runs.
index += 1updates progress.- 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:
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:
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.
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:
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:
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:
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:
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:
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.
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.
for (const item of items) {
console.log(item);
}
Use for...of for iterable values such as arrays, strings, maps, and sets.
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:
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:
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.lengthvisits 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
constfor an accumulator/counter: useletbecause reassignment is intentional. - Mutating loop bounds unexpectedly: avoid changing array length while traversing it in beginner code.
continuebefore awhileupdate: this can skip progress and create an infinite loop. Update before continuing or choose aforloop.- 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
letfor counters and accumulators; useconstfor current values that are not rebound. - Use descriptive names such as
index,total, andreading. - 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...ofwhen the index is unnecessary. - Use
breakfor a genuine stop andcontinuefor 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
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;
}
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
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.
- Name the four moving parts of a
forloop. - Why is
< array.lengththe normal array boundary? - How do
breakandcontinuediffer? - What creates an infinite loop?
- When is
for...ofpreferable?
Official References
- MDN: Loops and iteration
- MDN:
for - MDN:
while - MDN:
break - MDN:
continue - ECMA-262: iteration statements
References checked 2026-08-24.
