Chapter 6: JS Loops & Iteration
Loops are the engine of automated computation in JavaScript. They eliminate redundant code by repeatedly executing blocks of logic over datasets, processing array items, polling asynchronous events, and managing algorithmic state until exit conditions are met.
6.1 The Classic for Loop
The for statement creates a loop with three optional expressions enclosed in parentheses and
separated by semicolons:
// loop body executed on each iteration
}
- Initialization: Executed once before the loop begins. Used to
declare and initialize counter variables (e.g.,
let i = 0). - Condition: Evaluated before every iteration. If
true, the loop body runs. Iffalse, the loop terminates. - Final Expression: Evaluated after the loop body runs, before the
next condition check. Commonly used to step or increment (e.g.,
i++).
Example: Forward and Backward Traversal
// 1. Basic counting loop
for (let i = 1; i <= 5; i++) {
console.log(`Counter: ${i}`);
}
// 2. Traversing an array backwards
const frameworks = ["React", "Vue", "Angular", "Svelte"];
for (let i = frameworks.length - 1; i >= 0; i--) {
console.log(`Index ${i}: ${frameworks[i]}`);
}
// 3. Incrementing with custom step intervals
for (let percent = 0; percent <= 100; percent += 25) {
console.log(`Progress: ${percent}%`);
}
Try it Yourself »
Critical Scoping Rule:
let vs var in Loops
Always use let instead of var for your loop counter. let creates a new
block-scoped binding for every single iteration. If you use var, the counter is
function-scoped, meaning asynchronous callbacks or event listeners inside the loop will all reference the
final mutated value!
6.2 The while and do...while Loops
When the exact number of iterations is unknown in advance—such as awaiting user interaction, reading a network
stream, or generating random values until a threshold is reached—while loops provide flexible
condition-based iteration.
Entry-Controlled: Tests the condition before executing the block. If false initially, it never executes.
let fuel = 3;
while (fuel > 0) {
console.log(`Thrusting... Fuel: ${fuel}`);
fuel--;
}
// If fuel was 0 initially, runs 0 times
Exit-Controlled: Executes the block at least once, then checks the condition at the end.
let attempts = 0;
do {
attempts++;
console.log(`Attempt #${attempts}`);
} while (attempts < 1);
// Always runs at least 1 time guaranteed
Real-World Example: Rolling Dice Until Lucky 7
let rolls = 0;
let diceOutcome = 0;
while (diceOutcome !== 7) {
diceOutcome = Math.floor(Math.random() * 10) + 1; // 1 to 10
rolls++;
console.log(`Roll #${rolls}: Generated ${diceOutcome}`);
}
console.log(`🎉 Hit Lucky 7 after ${rolls} roll(s)!`);
▶ Try it Yourself »
6.3 Loop Lifecycle Architecture & Nested Loops
Before introducing fine-grained loop control statements like break and continue, it is
vital to master how JavaScript orchestrates its lifecycle pipeline under the hood, and how
multi-dimensional structures are traversed through nested iteration.
The loop evaluates its exit criteria before each cycle. break immediately diverts control to
termination, while continue skips directly to the next increment step.
Nested Loops & 2D Matrix Traversal
A nested loop is a loop statement running inside the body of another enclosing loop. Nested loops are fundamental when working with two-dimensional datasets like coordinate grids, image pixel buffers, game boards, or tabular spreadsheets.
The Fundamental Rule of Nested Iteration
For every single iteration of the outer loop, the inner loop executes its
entire sequence from start to finish. If the outer loop iterates 3 times and the inner loop
iterates 4 times, the inner block executes a total of 3 × 4 = 12 times.
for (let r = 0; r < 3; r++) {
// Inner Loop (Columns: c = 0, 1, 2, 3) executes completely for each r
for (let c = 0; c < 4; c++) {
console.log(`Row: ${r}, Col: ${c}`); // Fires 12 total times!
}
}
Real-World Example: Generating a Coordinate Matrix
// Generating a 3x3 Coordinate Matrix
const matrix = [];
const ROWS = 3;
const COLS = 3;
for (let r = 0; r < ROWS; r++) {
const row = [];
for (let c = 0; c < COLS; c++) {
row.push(`[${r},${c}]`);
}
matrix.push(row);
console.log(`Row ${r}: ${row.join(" ")}`);
}
// Console Output:
// Row 0: [0,0] [0,1] [0,2]
// Row 1: [1,0] [1,1] [1,2]
// Row 2: [2,0] [2,1] [2,2]
▶ Try it Yourself »
Algorithmic Complexity Alert: Beware of $O(n^2)$
Nested loops multiply processing cycles: nesting an $N$-length loop inside another $N$-length loop yields quadratic time complexity ($O(N^2)$). If $N = 10{,}000$, the inner body executes $100{,}000{,}000$ times, which blocks the single JavaScript thread and freezes user interfaces. Always keep nested loops shallow and consider indexed Hash Maps or Sets for large-scale data lookups!
6.4 Loop Control: break, continue & Labels
Fine-grained loop control statements permit stopping loops ahead of schedule or skipping unwanted iterations without nested conditionals:
Immediately halts execution and transfers control to the statement following the loop.
for (let i = 1; i <= 10; i++) {
if (i === 5) break; // Halts at 5
console.log(i); // Outputs: 1, 2, 3, 4
}
Skips the rest of the current iteration and jumps directly to the increment step.
for (let i = 1; i <= 5; i++) {
if (i % 2 === 0) continue; // Skip evens
console.log(i); // Outputs: 1, 3, 5
}
Advanced: Breaking Nested Loops with Labels
By default, break only exits the innermost loop. Prefixing an outer loop with a
label allows an inner loop to break or continue the outer loop directly!
// Matrix Search with Labelled Break
const matrix = [
[1, 2, 3],
[4, 99, 6],
[7, 8, 9]
];
const target = 99;
let foundCoords = null;
searchGrid: for (let r = 0; r < matrix.length; r++) {
for (let c = 0; c < matrix[r].length; c++) {
if (matrix[r][c] === target) {
foundCoords = { row: r, col: c };
break searchGrid; // Breaks OUT of BOTH loops immediately!
}
}
}
console.log(`Found ${target} at Row ${foundCoords.row}, Col ${foundCoords.col}`);
▶ Try it Yourself »
6.5 Modern Iteration: for...of vs for...in
ES6 introduced modern iterator constructs designed to replace verbose index counter indexing with clean, declarative iteration:
| Loop Feature | for...of (Iterables) |
for...in (Object Keys) |
|---|---|---|
| Iterates Over | Values of an iterable collection | Keys / Property Names of an object |
| Supported Types | Arrays, Strings, Sets, Maps, NodeLists | Plain JavaScript Objects, enumerable properties |
| Break / Continue? | Supported | Supported |
| Best For | Iterating array elements cleanly without index tracking | Inspecting dynamic object property names & dictionaries |
Code Comparison
// 1. for...of iterates VALUES
const technologies = ["JavaScript", "TypeScript", "Node.js"];
for (const tech of technologies) {
console.log(`Mastering: ${tech}`); // Direct values
}
// 2. for...in iterates KEYS
const student = { name: "Alex", score: 94, passed: true };
for (const key in student) {
console.log(`${key} => ${student[key]}`);
}
▶ Try it Yourself »
Golden Rule for Arrays
Never use for...in on Arrays! for...in iterates over all enumerable properties in
arbitrary order (including custom prototype extensions) and converts indices to strings. Always use
for...of or standard for for arrays.
6.6 Interactive Loop Playground & Step-by-Step Visualizer
Configure a loop configuration below and observe how the counter, accumulator, and conditions update on each iteration in real time:
6.7 Common Pitfalls: The Infinite Loop Threat
JavaScript is single-threaded. When an infinite loop executes, the browser's JavaScript engine is pinned at 100% CPU usage, completely blocking the Call Stack, preventing all DOM renders, user clicks, and network responses until the browser tab crashes.
// NEVER DO THIS: Missing counter increment!
let i = 0;
while (i < 5) {
console.log(i);
// Missing i++! Condition (0 < 5) is ALWAYS true!
}
// Guaranteed progression towards exit condition
let i = 0;
while (i < 5) {
console.log(i);
i++; // Increment steps towards false condition
}
Example: Safe Loop with Guaranteed Termination
// ✅ Always guarantee the loop will exit
let i = 0;
while (i < 5) {
console.log(i); // Outputs: 0, 1, 2, 3, 4
i++; // Each iteration moves i toward making (i < 5) false
}
// When i = 5, condition (5 < 5) evaluates to false → loop exits cleanly
▶ Try it Yourself »
6.8 Hands-on Challenge: Prime Number & FizzBuzz Matrix Filter
Write a function filterNumericMatrix(matrix) that iterates through a 2D matrix of numbers and
constructs an analytics report with:
Requirements:
- Use nested loops to inspect each cell in the 2D matrix.
- Calculate the
totalSumof all elements. - Collect all prime numbers into an array
primes. (A prime number is greater than 1 and divisible only by 1 and itself). - Apply a FizzBuzz label count: count how many numbers are multiples of both 3 and 5
(
fizzBuzzCount). - If any number is negative (
num < 0), immediatelycontinueto skip processing that cell.
Need a Hint?
isPrime(n) that returns false if n <= 1,
then checks divisibility from 2 up to Math.sqrt(n). Iterate the outer loop over rows
and the inner loop over columns with
for (const row of matrix) { for (const num of row) { ... } }.
Reveal Full Solution
// Helper: Checks if a number is prime using square-root optimization
function isPrime(num) {
if (num <= 1) return false;
if (num <= 3) return true;
if (num % 2 === 0 || num % 3 === 0) return false;
for (let i = 5; i * i <= num; i += 6) {
if (num % i === 0 || num % (i + 2) === 0) return false;
}
return true;
}
function filterNumericMatrix(matrix) {
let totalSum = 0;
const primes = [];
let fizzBuzzCount = 0;
for (let r = 0; r < matrix.length; r++) {
for (let c = 0; c < matrix[r].length; c++) {
const val = matrix[r][c];
// Skip negative numbers
if (val < 0) continue;
totalSum += val;
if (isPrime(val)) {
primes.push(val);
}
if (val % 3 === 0 && val % 5 === 0 && val !== 0) {
fizzBuzzCount++;
}
}
}
return { totalSum, primes, fizzBuzzCount };
}
// Test Matrix
const sampleGrid = [
[3, 5, 15],
[7, 11, -4],
[30, 2, 4]
];
console.log(filterNumericMatrix(sampleGrid));
// Expected output:
// {
// totalSum: 67,
// primes: [3, 5, 7, 11, 2],
// fizzBuzzCount: 2 // 15 and 30
// }
▶ Try it Yourself »