|$ curl https://forge-ai.dev/api/markdown?path=docs/js/loops
$cat docs/javascript-—-loops-&-iteration.md
updated This week·25 min read·published

JavaScript — Loops & Iteration

JavaScriptLoopsBeginnerBeginner to Advanced
Introduction

Loops are fundamental control structures that execute a block of code repeatedly until a specified condition evaluates to false. JavaScript provides multiple loop constructs, each suited for different scenarios: for, while, do...while, for...in, and for...of.

Modern JavaScript also offers array iteration methods (forEach, map, filter, reduce) that often replace traditional loops with more declarative code. Understanding when to use each loop type and iteration method is essential for writing efficient, readable JavaScript.

This page covers every loop construct in detail, their performance characteristics, common patterns, and best practices. We will also explore break, continue, labeled statements, and the modern iteration protocols.

loops-intro.js
JavaScript
1// A loop repeats code until a condition is false
2// Basic for loop — classic counter-based iteration
3for (let i = 0; i < 5; i++) {
4 console.log("Iteration:", i);
5}
6// Output: 0, 1, 2, 3, 4
7
8// While loop — runs while condition is true
9let count = 0;
10while (count < 5) {
11 console.log("Count:", count);
12 count++;
13}
14// Output: 0, 1, 2, 3, 4
15
16// do...while — always runs at least once
17let x = 0;
18do {
19 console.log("x:", x);
20 x++;
21} while (x < 3);
22// Output: 0, 1, 2
23
24// Modern JavaScript favors declarative iteration
25const numbers = [10, 20, 30, 40, 50];
26
27// for...of — iterate over iterable values
28for (const num of numbers) {
29 console.log(num); // 10, 20, 30, 40, 50
30}
31
32// Array methods — often preferred
33numbers.forEach((num, i) => {
34 console.log(`Index ${i}: ${num}`);
35});
36const doubled = numbers.map(n => n * 2);
37const evens = numbers.filter(n => n % 2 === 0);
38const sum = numbers.reduce((acc, n) => acc + n, 0);
The for Loop

The for loop is the most traditional and flexible loop in JavaScript. It consists of three optional expressions: initialization, condition, and final expression. The loop body executes while the condition is truthy.

for-loop.js
JavaScript
1// Standard for loop — initialize, condition, increment
2for (let i = 0; i < 10; i++) {
3 console.log(i);
4}
5
6// Multiple variables in initialization
7for (let i = 0, j = 10; i < j; i++, j--) {
8 console.log(i, j);
9}
10
11// Empty initialization (variables declared outside)
12let k = 0;
13for (; k < 5; k++) {
14 console.log(k);
15}
16
17// Infinite loop — condition is omitted (truthy by default)
18// for (;;) { console.log("forever"); }
19
20// Reverse iteration
21for (let i = 10; i >= 0; i--) {
22 console.log(i);
23}
24
25// Stepping by 2, 5, etc.
26for (let i = 0; i < 20; i += 3) {
27 console.log("Step 3:", i);
28}
29
30// Iterating an array with index
31const fruits = ["apple", "banana", "cherry"];
32for (let i = 0; i < fruits.length; i++) {
33 console.log(`${i}: ${fruits[i]}`);
34}
35
36// Caching array length for performance (rarely matters now)
37for (let i = 0, len = fruits.length; i < len; i++) {
38 console.log(fruits[i]);
39}
40
41// Nested for loops — multi-dimensional arrays
42const matrix = [
43 [1, 2, 3],
44 [4, 5, 6],
45 [7, 8, 9],
46];
47for (let row = 0; row < matrix.length; row++) {
48 for (let col = 0; col < matrix[row].length; col++) {
49 console.log(`matrix[${row}][${col}] = ${matrix[row][col]}`);
50 }
51}
52
53// for loop without body — for side effects only
54let arr = [];
55for (let i = 0; i < 5; arr.push(i++));
56console.log(arr); // [0, 1, 2, 3, 4]
57
58// Loop with continue to skip iterations
59for (let i = 0; i < 10; i++) {
60 if (i % 2 === 0) continue; // skip evens
61 console.log("Odd:", i); // 1, 3, 5, 7, 9
62}
63
64// Loop with break to exit early
65for (let i = 0; i < 100; i++) {
66 if (i === 5) break;
67 console.log(i); // 0, 1, 2, 3, 4
68}

info

The for loop is extremely flexible but also verbose. In modern JavaScript, prefer for...of for iterating over values and array methods (forEach, map) for most iteration needs. Reserve the classic for loop for cases where you need fine-grained control over the index, custom stepping, or complex conditions.
while and do...while

The while loop evaluates a condition before each iteration; if the condition is falsy initially, the body never executes. The do...while loop evaluates the condition after the body, guaranteeing at least one execution. Use while loops when the number of iterations is not known in advance.

while-do-while.js
JavaScript
1// while loop — condition checked before each iteration
2let i = 0;
3while (i < 5) {
4 console.log("while:", i);
5 i++;
6}
7
8// while loop with complex condition
9let attempts = 0;
10let maxAttempts = 3;
11let connected = false;
12
13while (attempts < maxAttempts && !connected) {
14 console.log(`Attempt ${attempts + 1}`);
15 connected = tryConnect(); // returns true/false
16 attempts++;
17}
18
19// do...while — always runs at least once
20let j = 0;
21do {
22 console.log("do-while:", j);
23 j++;
24} while (j < 5);
25
26// Guaranteed execution with do...while
27let input;
28do {
29 input = prompt("Enter a number greater than 0:");
30} while (input <= 0);
31
32// while loop for reading from a stream
33async function readStream(reader) {
34 let result = "";
35 while (true) {
36 const { done, value } = await reader.read();
37 if (done) break;
38 result += value;
39 }
40 return result;
41}
42
43// while with sentinel value
44function findFirstNegative(numbers) {
45 let i = 0;
46 while (i < numbers.length && numbers[i] >= 0) {
47 i++;
48 }
49 return i < numbers.length ? i : -1;
50}
51
52// Infinite loop with break inside
53let counter = 0;
54while (true) {
55 counter++;
56 if (counter >= 100) break;
57 if (counter % 10 === 0) {
58 console.log("Multiple of 10:", counter);
59 }
60}
61
62// do...while for retry logic
63let success = false;
64let retries = 0;
65do {
66 try {
67 success = performOperation();
68 } catch (e) {
69 console.error("Attempt ${retries + 1} failed");
70 success = false;
71 }
72 retries++;
73} while (!success && retries < 3);

best practice

Use while when you need to check a condition before starting (most common). Use do...while when the body must execute at least once regardless of the condition — such as prompting for valid input or retrying operations. Be careful with infinite loops: always ensure the condition will eventually become false.
for...in — Object Properties

The for...in loop iterates over all enumerable string properties of an object, including inherited ones from the prototype chain. It is designed for objects, not arrays. Using it on arrays can produce unexpected results because it iterates over enumerable properties (including array indices and any custom properties added to the array).

for-in.js
JavaScript
1// for...in iterates over object keys
2const user = {
3 name: "Alice",
4 age: 30,
5 role: "admin",
6};
7
8for (const key in user) {
9 console.log(`${key}: ${user[key]}`);
10}
11// Output: name: Alice
12// age: 30
13// role: admin
14
15// Checking own vs inherited properties
16class Animal {
17 constructor(name) {
18 this.name = name;
19 }
20 speak() {
21 console.log(`${this.name} makes a sound`);
22 }
23}
24
25class Dog extends Animal {
26 constructor(name, breed) {
27 super(name);
28 this.breed = breed;
29 }
30}
31
32const dog = new Dog("Rex", "German Shepherd");
33
34for (const key in dog) {
35 console.log(key); // "name", "breed"
36 // Note: "speak" is on the prototype, not enumerable
37}
38
39// Use hasOwnProperty to filter inherited properties
40for (const key in dog) {
41 if (Object.hasOwn(dog, key)) {
42 console.log("Own property:", key, dog[key]);
43 }
44}
45
46// for...in on arrays — iterate over indices (strings!)
47const arr = ["a", "b", "c"];
48arr.customProp = "hello";
49
50for (const index in arr) {
51 console.log(index, typeof index); // "0" (string), "1", "2", "customProp"
52}
53
54// Better: use for...of for arrays
55for (const value of arr) {
56 console.log(value); // "a", "b", "c" (only actual values)
57}
58
59// Enumerating object keys with Object.keys()
60Object.keys(user).forEach(key => {
61 console.log(key, user[key]);
62});
63
64// Enumerating key-value pairs
65Object.entries(user).forEach(([key, value]) => {
66 console.log(`${key}: ${value}`);
67});
68
69// for...in with null/undefined prototype
70const dict = Object.create(null);
71dict.foo = "bar";
72for (const key in dict) {
73 console.log(key); // "foo" — works, no prototype pollution
74}

warning

Never use for...in on arrays. It iterates over all enumerable properties (including inherited ones and custom properties), returns string keys instead of numbers, and can include properties added to the prototype. Use for...of, forEach, or a classic for loop for arrays. Reserve for...in for plain objects, and pair it with Object.hasOwn() to filter inherited properties.
for...of — Iterables

Introduced in ES2015 (ES6), the for...of loop iterates over iterable objects — Arrays, Strings, Maps, Sets, TypedArrays, NodeLists, generators, and any object implementing the [Symbol.iterator] protocol. It yields the values directly, not the indices or keys.

for-of.js
JavaScript
1// for...of iterates over VALUES (not indices)
2const colors = ["red", "green", "blue"];
3
4for (const color of colors) {
5 console.log(color); // "red", "green", "blue"
6}
7
8// With index — use .entries()
9for (const [index, color] of colors.entries()) {
10 console.log(`${index}: ${color}`);
11}
12
13// Iterating over a string
14for (const char of "hello") {
15 console.log(char); // "h", "e", "l", "l", "o"
16}
17
18// for...of respects Unicode code points
19for (const char of "🌟🔥🎉") {
20 console.log(char); // works correctly with emoji
21}
22
23// Iterating over a Map
24const userMap = new Map([
25 ["name", "Alice"],
26 ["age", 30],
27 ["role", "admin"],
28]);
29
30for (const [key, value] of userMap) {
31 console.log(`${key}: ${value}`);
32}
33
34// Iterating over a Set
35const uniqueNumbers = new Set([1, 2, 3, 2, 1]);
36for (const num of uniqueNumbers) {
37 console.log(num); // 1, 2, 3 (duplicates removed)
38}
39
40// Iterating over a NodeList (DOM)
41// const paragraphs = document.querySelectorAll("p");
42// for (const p of paragraphs) {
43// p.style.color = "#00FF41";
44// }
45
46// Iterating over arguments
47function sum() {
48 let total = 0;
49 for (const num of arguments) {
50 total += num;
51 }
52 return total;
53}
54console.log(sum(1, 2, 3, 4, 5)); // 15
55
56// Destructuring in for...of
57const points = [
58 { x: 1, y: 2 },
59 { x: 3, y: 4 },
60 { x: 5, y: 6 },
61];
62
63for (const { x, y } of points) {
64 console.log(`Point: (${x}, ${y})`);
65}
66
67// for...of with generators and async iterators
68function* range(start, end) {
69 for (let i = start; i <= end; i++) {
70 yield i;
71 }
72}
73
74for (const n of range(1, 5)) {
75 console.log(n); // 1, 2, 3, 4, 5
76}
77
78// Early exit with break
79for (const item of hugeArray) {
80 if (item.id === targetId) {
81 console.log("Found:", item);
82 break;
83 }
84}
85
86// Skipping with continue
87for (const item of items) {
88 if (item.isInactive) continue;
89 process(item);
90}
🔥

pro tip

for...of is the most versatile loop in modern JavaScript. It works with any iterable, supports break/continue, and pairs well with destructuring. Unlike forEach, it supports early exit and works with async iterables using for await...of. Make it your default loop for arrays and iterables.
preview
Break, Continue & Labels

JavaScript provides keywords to control loop execution from within the body: break terminates the loop entirely, continue skips the current iteration and proceeds to the next, and labels allow breaking or continuing nested loops from an outer scope.

break-continue.js
JavaScript
1// break — exit the loop immediately
2for (let i = 0; i < 10; i++) {
3 if (i === 5) {
4 break; // stops at 5
5 }
6 console.log(i); // 0, 1, 2, 3, 4
7}
8
9// continue — skip this iteration
10for (let i = 0; i < 10; i++) {
11 if (i % 2 === 0) {
12 continue; // skip even numbers
13 }
14 console.log(i); // 1, 3, 5, 7, 9
15}
16
17// break in while loop
18let found = false;
19let index = 0;
20while (index < items.length && !found) {
21 if (items[index].id === targetId) {
22 found = true;
23 break;
24 }
25 index++;
26}
27
28// continue skips to the next iteration of while
29let i = 0;
30while (i < 10) {
31 i++;
32 if (i % 3 !== 0) continue;
33 console.log("Multiple of 3:", i);
34}
35
36// Labeled break — exit outer loop from inner loop
37outerLoop: for (let i = 0; i < 3; i++) {
38 for (let j = 0; j < 3; j++) {
39 if (i === 1 && j === 1) {
40 break outerLoop; // breaks BOTH loops
41 }
42 console.log(`i=${i}, j=${j}`);
43 }
44}
45// Output: (0,0), (0,1), (0,2), (1,0) — then breaks
46
47// Labeled continue — skip outer iteration
48outerContinue: for (let i = 0; i < 3; i++) {
49 for (let j = 0; j < 3; j++) {
50 if (j === 1) {
51 continue outerContinue; // skip to next i
52 }
53 console.log(`i=${i}, j=${j}`);
54 }
55}
56// Output: (0,0), (1,0), (2,0) — skips j=1 and j=2 for each i
57
58// Using return to exit a loop in a function
59function findFirstEven(numbers) {
60 for (const num of numbers) {
61 if (num % 2 === 0) {
62 return num; // exits both loop and function
63 }
64 }
65 return null;
66}
67
68// break with forEach — NOT possible (forEach doesn't support break)
69// Use for...of or some() instead
70const foundItem = items.some(item => {
71 if (item.id === targetId) {
72 console.log("Found via some()");
73 return true; // breaks the iteration
74 }
75 return false;
76});
77
78// Using every() to simulate break (return false to stop)
79items.every(item => {
80 console.log(item);
81 return item.id !== targetId; // stops when id matches
82});

info

Labeled loops are rarely needed but are powerful when they are. Use them only when you need to break out of (or continue) a specific outer loop from a deeply nested inner loop. In most cases, extracting the nested logic into a function and using return is cleaner. Remember that forEach does not support break or continue — use for...of if you need them.
Performance Considerations

Loop performance in JavaScript has evolved significantly as engines (V8, SpiderMonkey, JavaScriptCore) have become highly optimized. In most cases, the choice of loop construct has negligible impact on real-world performance. However, certain patterns can be measurably faster for large datasets or performance-critical paths.

performance.js
JavaScript
1// Performance comparison of loop types
2// Modern JS engines optimize all loops well
3
4// For comparison, iterate a large array
5const BIG = 1_000_000;
6const arr = Array.from({ length: BIG }, (_, i) => i);
7
8// Classic for loop — often fastest for simple numeric iteration
9console.time("for");
10let sum1 = 0;
11for (let i = 0; i < arr.length; i++) {
12 sum1 += arr[i];
13}
14console.timeEnd("for");
15
16// for...of — slightly slower but more readable
17console.time("for-of");
18let sum2 = 0;
19for (const val of arr) {
20 sum2 += val;
21}
22console.timeEnd("for-of");
23
24// forEach — similar to for-of
25console.time("forEach");
26let sum3 = 0;
27arr.forEach(val => { sum3 += val; });
28console.timeEnd("forEach");
29
30// Cached length — negligible benefit in modern engines
31console.time("for-cached");
32let sum4 = 0;
33for (let i = 0, len = arr.length; i < len; i++) {
34 sum4 += arr[i];
35}
36console.timeEnd("for-cached");
37
38// Reverse while — sometimes fastest
39console.time("while-reverse");
40let sum5 = 0;
41let i = arr.length;
42while (i--) {
43 sum5 += arr[i];
44}
45console.timeEnd("while-reverse");
46
47// Performance tips that still matter:
48// 1. Avoid DOM access inside loops
49// BAD:
50for (let i = 0; i < items.length; i++) {
51 document.getElementById("result").innerHTML += items[i];
52}
53// GOOD: batch DOM updates
54let html = "";
55for (const item of items) {
56 html += item;
57}
58document.getElementById("result").innerHTML = html;
59
60// 2. Avoid creating functions inside loops
61// BAD (creates a new function each iteration):
62for (const item of items) {
63 process(item, function(result) {
64 console.log(result);
65 });
66}
67// GOOD: define function once
68function handleResult(result) {
69 console.log(result);
70}
71for (const item of items) {
72 process(item, handleResult);
73}
74
75// 3. Use appropriate data structures
76// Searching with Set is O(1) vs Array O(n)
77const idSet = new Set(largeArray.map(x => x.id));
78for (const item of anotherArray) {
79 // BAD: largeArray.some(x => x.id === item.id) — O(n)
80 // GOOD: idSet.has(item.id) — O(1)
81 if (idSet.has(item.id)) {
82 // process
83 }
84}
85
86// 4. Prefer for...of with break for early termination
87function findFirst(items, predicate) {
88 for (const item of items) {
89 if (predicate(item)) return item;
90 }
91 return null;
92}
📝

note

The performance differences between loop types are usually micro-optimizations — typically a few percent difference. Write code for readability first, then profile before optimizing. The biggest performance wins come from reducing algorithmic complexity (e.g., O(n²) to O(n)), avoiding unnecessary work inside loops, and using appropriate data structures — not from choosing for over for...of.
Best Practices

Choosing the right loop and using it correctly makes your code cleaner, safer, and more maintainable. Here are the best practices for JavaScript loops and iteration.

Use for...of as your default loop for arrays and iterables — it's readable and supports break/continue
Use array methods (map, filter, reduce, find, some, every) for declarative data transformation
Never use for...in on arrays — it returns string keys and includes inherited properties
Use for...in only on plain objects, and pair with Object.hasOwn() to filter prototype properties
Use forEach for simple side-effect iteration when you don't need break/continue and don't care about the return value
Cache array length in a local variable only when performance profiling shows it matters
Avoid creating functions (closures) inside loop bodies — define them outside and reference them
Use labeled break/continue sparingly — extract inner loops to functions instead
Prefer while loops when the number of iterations is unknown (streams, user input, retries)
Use do...while when the body must execute at least once (input validation, retry logic)

best practice

The modern JavaScript developer's loop hierarchy: 1) Array methods (map, filter, reduce) for data transformation — declarative and chainable. 2) for...of for general iteration — flexible, supports break/continue, works with any iterable. 3) Classic for/while when you need index-based control or the exact number of iterations is dynamic or complex.

Choosing the Right Iteration Tool

choosing-iteration.js
JavaScript
1// Decision guide for iteration tools
2
3const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
4
5// When you need to TRANSFORM each element
6const doubled = data.map(n => n * 2);
7// Result: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
8
9// When you need to FILTER elements
10const evens = data.filter(n => n % 2 === 0);
11// Result: [2, 4, 6, 8, 10]
12
13// When you need to REDUCE to a single value
14const sum = data.reduce((acc, n) => acc + n, 0);
15// Result: 55
16
17// When you need to FIND a single element
18const firstOver5 = data.find(n => n > 5);
19// Result: 6
20
21// When you need to CHECK if some/every element matches
22const hasEven = data.some(n => n % 2 === 0); // true
23const allPositive = data.every(n => n > 0); // true
24
25// When you need side effects with break support
26for (const n of data) {
27 if (n > 7) break; // early exit supported
28 console.log(n);
29}
30
31// When you need the index
32for (const [i, n] of data.entries()) {
33 console.log(`data[${i}] = ${n}`);
34}
35
36// When you need both key and value on an object
37const config = { theme: "dark", lang: "en", debug: false };
38for (const [key, value] of Object.entries(config)) {
39 console.log(`${key}: ${value}`);
40}
41
42// When you need a custom iteration pattern
43function* chunked(array, size) {
44 for (let i = 0; i < array.length; i += size) {
45 yield array.slice(i, i + size);
46 }
47}
48for (const chunk of chunked(data, 3)) {
49 console.log(chunk); // [1,2,3], [4,5,6], [7,8,9], [10]
50}
$Blueprint — Engineering Documentation·Section ID: JS-LOOPS·Revision: 1.0