A classic for loop has three parts: for (let i = 0; i < arr.length; i++) { ... }. Use it when you need the index or a custom step. For plain values use for...of, for an object's keys use for...in or Object.entries, and use while when you do not know in advance how many rounds you need.
| Loop | Best for | Can break early? |
|---|---|---|
| for (let i = 0; i < n; i++) | Counting, index access, stepping backwards or by 2 | Yes |
| for (const x of iterable) | Values of arrays, strings, Maps, Sets, NodeLists | Yes |
| for (const key in obj) | Keys of a plain object (includes inherited ones) | Yes |
| while (condition) | Repeat until something changes, unknown count | Yes |
| do { } while (condition) | Same, but always runs at least once | Yes |
| arr.forEach(fn) | Side effects on every array item | No (only by throwing) |
Updated
Every loop on this page repeats code, but they differ in what they hand you each round, what they can loop over and how you get out. The runner below lets you point each loop at four kinds of data and watch the real output, which makes mistakes like for...in on an array or for...of on a plain object easy to spot.
Change the start, the condition or the step to count any way you like.
const items = ["a", "b", "c", "d"];
for (let i = 0; i < items.length; i++) console.log(i, items[i]); // 0 a ... 3 d
for (let i = items.length - 1; i >= 0; i--) console.log(items[i]); // d c b a
for (let i = 0; i < items.length; i += 2) console.log(items[i]); // a centries() pairs each value with its position, so you get both without a counter.
for (const [i, name] of ["Ana", "Bruno"].entries()) {
console.log(`${i + 1}. ${name}`);
}
// 1. Ana
// 2. Brunodo...while checks its condition after the body, so the body runs once even when the condition starts false.
let attempts = 0;
while (attempts < 3) {
attempts++;
}
let n = 10;
do {
console.log(n); // logs 10 once
n++;
} while (n < 5);break grid stops both loops as soon as the first match is found.
const grid = [[1, 2], [3, 42], [5, 6]];
let at = null;
grid: for (let r = 0; r < grid.length; r++) {
for (let c = 0; c < grid[r].length; c++) {
if (grid[r][c] === 42) {
at = [r, c];
break grid;
}
}
}
console.log(at); // [1, 1]for...of pauses at each await. The same code inside forEach would start every request at once and not wait.
async function saveAll(rows) {
for (const row of rows) {
await fetch("/api/rows", { method: "POST", body: JSON.stringify(row) });
}
}
The first part runs once before anything else and usually creates the counter. The second is checked before every round, and the loop ends as soon as it is false. The third runs after each round, usually to move the counter on.
Any part can be left empty. for (;;) is a loop that only ends through break or return, which is occasionally useful for retry logic with a clear exit inside.
break leaves the innermost loop immediately. continue skips the rest of the current round and moves on to the next one. Both work in for, for...of, for...in, while and do...while, but not in a forEach callback, where return only ends that one call.
A label is a name followed by a colon in front of a statement. break label and continue label then target that outer statement, which saves you from managing a found flag across nested loops.
for...of gives you values from anything iterable, such as the items of an array or the characters of a string. for...in gives you property names as strings, including inherited enumerable ones. On an array, for...in yields '0', '1', '2' rather than the items.
You cannot, except by throwing an error. Switch to for...of, which supports break, or to some, every, find or findIndex, which stop on their own once they have an answer.
Use for (const [key, value] of Object.entries(obj)). It covers only the object's own keys and gives you both parts. Using for...of directly on a plain object throws TypeError: obj is not iterable.
Put a label before the outer loop and use it with break: outer: for (...) { for (...) { if (found) break outer; } }. continue outer jumps to the next round of the outer loop instead.
Start at the last index and count down: for (let i = arr.length - 1; i >= 0; i--). This is also the safe way to remove items with splice while looping, since the positions you have yet to visit do not shift.
Related: JavaScript array methods, JavaScript objects guide, Async and fetch in JavaScript, JavaScript event listeners, JavaScript formatter.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.