Looping statement enable our JavaScript program to continuously execute a piece of code as long as a specified condition is true. Looping statement makes the JavaScript code compact. There are besically five types of looping statements-
A for loop repeats a piece of code until a specified condition is true. The for Loop has three important parts i.e. Initialization, Condition, and Increment/Decrement.
for (Initialization; Condition; Increment/Decrement) {
...........
}
for (i = 0; i <=5; i++) {
console.log(i);
}
The while loop is a more simple version of the for, which repeats a piece of code until a specified condition is true.
let i = 10;
while (i > 0) {
console.log(i);
i--;
}
The do-while loop is very similar to the while loop, but it executed at least once whether the condition is true or false, because condition check happens at the end of the loop.
let i = 0;
do {
i++;
console.log(i);
} while (i <= 5);
The for-in loop is a special kind of a loop in JavaScript which iterates over the properties name of an object, or the elements of an array.
let datas = [3, 5, 7, 9, 11];
for (var data in datas) {
console.log(data); // 0, 1, 2, 3, 4
}
A `for...of` loop iterates values from an iterable such as an array, string, map, set, or generator. Use `for...in` for enumerable property keys, not array values.
let datas = [3, 5, 7, 9, 11];
for (var data of datas) {
console.log(data); // 3, 5, 7, 9, 11
}
The break statement exits a loop immediately. The continue statement skips the current iteration and moves to the next one.
// break - exit loop when 5 is found
for (let i = 0; i < 10; i++) {
if (i === 5) break;
console.log(i); // 0, 1, 2, 3, 4
}
// continue - skip even numbers
for (let i = 0; i <= 10; i++) {
if (i % 2 === 0) continue;
console.log(i); // 1, 3, 5, 7, 9
}
// Nested loop with labeled break
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (j === 1) break outer; // breaks both loops
console.log(`i=${i}, j=${j}`);
}
}
// Output: i=0, j=0
ES5+ provides functional iteration methods that are often cleaner than traditional loops for working with arrays.
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// forEach - iterate without return value
numbers.forEach((n, index) => console.log(`[${index}] = ${n}`));
// map - transform each element
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2,4,6,8,10,12,14,16,18,20]
// filter - keep elements matching condition
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2,4,6,8,10]
// reduce - accumulate to single value
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 55
// find - first match
const firstOver5 = numbers.find(n => n > 5);
console.log(firstOver5); // 6
// some / every
console.log(numbers.some(n => n > 9)); // true
console.log(numbers.every(n => n > 0)); // true
Different loops are better for different situations. Choosing the right one makes your code easier to read and helps avoid common mistakes.
| Loop Type | Best Use Case | Notes |
|---|---|---|
| for | When you know the start, stop, and step. | Best for indexed iteration and counting. |
| while | When repetition depends on a condition. | Useful when the number of iterations is unknown beforehand. |
| do...while | When the body must run at least once. | Good for menu loops and retry prompts. |
| for...in | Iterating over object keys. | Avoid using it for normal array value iteration. |
| for...of | Iterating over iterable values. | Ideal for arrays, strings, Maps, and Sets. |
| Array methods | Transforming or filtering arrays. | Use map(), filter(), reduce(), etc. for readable functional code. |
Use a `for` loop when initialization, continuation condition, and update belong together. Use `while` when continuation depends on state changed by several operations, and `do...while` only when the body must run before the first test. Write the condition from the invariant and termination rule rather than copying an index template.
Before coding, state what has been processed, what remains, and why each iteration moves toward termination. An off-by-one bug usually comes from an unclear boundary: whether the end is inclusive, whether length is a count or last index, or whether the update occurs before or after access.
`break` exits the nearest loop or labeled statement; `continue` skips to the next iteration according to the loop form; `return` exits the entire function; `throw` transfers to an error boundary. Prefer a guard and continue for one exceptional item, but avoid many exits that make cleanup and invariants hard to follow.
A labeled break can leave nested loops without a separate flag, but a named helper that returns a result is often clearer. Choose the control structure that communicates the search, transformation, retry, traversal, or state-machine intent rather than minimizing lines.
`for...of` consumes an iterable and yields its values. Arrays, strings, maps, sets, typed arrays, and many DOM collections are iterable. A string iteration yields Unicode code points rather than UTF-16 code units, which is often safer for visible characters but still does not equal user-perceived grapheme clusters.
`for...in` enumerates enumerable string property keys, including inherited keys unless filtered. It is intended for object properties, not array values; array keys are strings and extra enumerable properties can appear. For ordinary own object entries, use `Object.keys`, `Object.values`, or `Object.entries` with a deliberate ordering expectation.
Array methods express common intents: `map` transforms each element, `filter` selects, `find` stops at one match, `some` and `every` short-circuit predicates, `reduce` accumulates, and `forEach` performs iteration without producing a result. Do not force a complex stateful algorithm into reduce when a named loop is clearer.
Custom iterables implement `Symbol.iterator` and return an iterator whose `next` produces `{ value, done }`. Generators simplify that protocol and can clean up through `return` behavior when iteration ends early. Document whether iteration is repeatable, consumes a resource, or reflects mutations made during traversal.
Changing a collection while traversing it can skip, repeat, or newly include items according to the collection and algorithm. Removing an array element shifts later indexes; appending can extend an index-based loop; Map and Set iteration have their own mutation semantics. Prefer building a new result or iterating a stable snapshot when correctness matters.
When in-place removal is required, iterate an array backward or maintain an index that advances only when no removal occurs. Explain the invariant and test consecutive matches. Avoid `delete` on array elements when a dense sequence is expected because it leaves an empty slot rather than shifting values.
Live DOM collections can change as matching nodes are inserted or removed, while static query results do not. Convert to an array or otherwise snapshot when the loop mutates the DOM. Batch DOM reads and writes to reduce forced layout, and use event delegation or observers instead of repeatedly scanning the entire document.
Shared mutable state can change between asynchronous iterations even though each synchronous job runs to completion. Capture the item identifier and revalidate current state before committing delayed work. Do not assume the collection still represents the same authorization, selection, or ordering after an await.
A `for...of` loop can await each item sequentially when order or capacity requires it. `for await...of` consumes async iterables and can also consume sync iterables through adaptation. Use it for streams or paginated producers that yield over time, and ensure early exit closes the iterator and underlying resource.
`forEach` does not await an async callback or collect its returned Promises. Use a sequential loop, `Promise.all` for a bounded independent set, or a concurrency-limited pool. Starting every operation with `map` before applying a limit is already unbounded; the limiter must control when each operation is created.
Optimize only after measuring. An algorithmic change from repeated linear lookup to a Set or Map matters more than replacing one loop syntax with another. Measure realistic collection size, key distribution, allocation, parsing, DOM work, and downstream calls. Keep correctness tests around any low-level optimization.
Long synchronous loops block input and rendering. Chunk non-urgent work with an appropriate scheduler, move suitable CPU work to a worker, or process a stream incrementally. Preserve cancellation, progress, and partial-result ownership. Yielding too frequently also adds overhead, so tune from responsiveness evidence.
Test an empty collection, one item, two items, the first and last match, no match, every item matching, consecutive removals, duplicate values, sparse arrays where supported, and the largest realistic input. For index ranges, test immediately below, at, and above each boundary. Verify the result and that input mutation matches the contract.
Property-based tests can assert invariants such as output length, preserved ordering, membership, sum, or round-trip behavior across many generated inputs. Include a maximum-iteration guard in tests for complex retry or state-machine loops so a termination defect fails quickly with diagnostic state rather than hanging the suite.
When a loop is slow, sample where time is spent instead of printing every iteration. Count iterations, allocations, lookups, awaits, DOM reads, and downstream calls. For async pools, test maximum observed concurrency, cancellation, partial failure, and resource cleanup. A correct final array does not prove capacity limits were respected while it was built.
Explore 500+ free tutorials across 20+ languages and frameworks.