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
Retrieve 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. Today, process lists with explicit loops so every iteration is visible.
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
A loop is a controlled replay button. Instead of writing five nearly identical statements, define where repetition starts, when it continues, what happens each pass, and how progress changes.
A for loop places those controls together:
for (let index = 0; index < 3; index += 1) {
console.log(index);
}
Trace it:
let index = 0runs once.index < 3is checked. If false, the loop ends.- The body runs.
index += 1updates progress.- Steps 2-4 repeat.
It logs 0, 1, and 2. The condition is false at 3. This aligns with zero-based array indexes: a list of length 3 has valid indexes 0 through 2, so index < list.length is the standard boundary.
A trace table is the safest beginner debugging tool. Create columns for the counter, condition result, current item, and accumulator after the body. Include a final row where the condition becomes false; that row explains why execution stops. If the expected number of passes is five, identify all five counter values before running the code. This catches a mistaken starting value or comparison without relying on trial and error.
A while loop exposes only the condition in its header:
let attempts = 0;
while (attempts < 3) {
console.log("Attempt", attempts + 1);
attempts += 1;
}
Choose for when initialization, update, and boundary form a clear counting sequence. Choose while when continuation depends on a state whose number of repetitions is not the main idea. Every while loop needs a believable path that makes its condition false.
Processing and accumulating
To summarize values, initialize an accumulator before the loop and update it inside:
const values = [4, 7, 2];
let total = 0;
for (let index = 0; index < values.length; index += 1) {
total += values[index];
}
The array remains const; the total is let because it is rebound each pass. values[index] selects the current item.
break, continue, and nesting
break leaves the loop when further work is unnecessary. continue skips only the current iteration. Use either sparingly; too many jumps make control flow difficult to trace.
A nested loop repeats an inner sequence for each outer pass, useful for a small grid. If each loop runs 3 times, the body runs 9 times. Work grows quickly, so nesting must have a clear purpose.
Worked Example: Number Summary
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 iteration, write a trace row containing index, currentNumber, total, evenCount, and largest. Initializing largest with the first element works for negative lists too; initializing it to 0 would fail for [-8, -3]. This example assumes a non-empty array. Empty-input handling belongs in validation logic.
Intermediate Example: Skip and Stop
Process readings, skip negative invalid readings, and stop at a sentinel value of 999:
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
-1 reaches continue, so accumulator updates are skipped. 999 reaches break, so 22 is never visited. Put stop conditions before skip conditions if a sentinel might otherwise satisfy a skip rule.
A simple nested-loop concept:
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. The inner loop completes all three columns for each row.
Optional Advanced Extension: for...of
When only values matter, for...of is clearer than manually indexing an array:
const prices = [100, 250, 50];
let total = 0;
for (const price of prices) {
total += price;
}
console.log(total); // 400
The per-iteration price binding is const because that binding is not reassigned during its pass. Do not use for...in for array values; it enumerates property keys and has different semantics.
Deep Dive: for, for...of, and for...in
Choose a loop based on what you are iterating.
for (let index = 0; index < items.length; index += 1) {
console.log(index, items[index]);
}
Use a classic for when you need explicit index control.
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. Do not use it as your default array loop.
do...while
do...while guarantees at least one execution.
let attempt = 0;
do {
attempt += 1;
} while (attempt < 3);
Nested-loop escape with labels
Labels exist but should be rare.
outer:
for (const row of grid) {
for (const cell of row) {
if (cell === target) {
break outer;
}
}
}
Usually a helper function, some, or find produces clearer code. Learn labels so you can read them, not so you can force them into every solution.
Mistakes and Debugging
- Infinite loop: the condition remains true because the update is missing, moves the wrong direction, or updates another 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 initializer, condition, and update on paper. Log the counter for a small input, but do not flood the console with an unbounded loop.
Test loops with an empty array, a one-element array, and a typical multi-element array. Empty input proves the body can run zero times safely. One element exposes incorrect starting indexes. Multiple elements checks updates and accumulation. For stop/skip logic, include a case where the sentinel is first, middle, last, and absent. These small 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. for gathers setup, continuation, and update; while emphasizes a state condition. Accumulators summarize values, while break stops and continue skips. Reliable loops always make 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.
