You learned the loop statements back in the Loops section. Since then you have met strings, arrays, objects, Sets, and Maps. This lesson pulls it all together: for each kind of data, which loop fits best, and why.
| Loop | Gives you | break / continue? |
|---|---|---|
| for (let i = 0; ...) | A counter you control | Yes |
| for...of | Each value of an iterable | Yes |
| for...in | Each key of an object | Yes |
| while / do...while | Repeats until a condition changes | Yes |
| array.forEach(fn) | Each value (and index) of an array | No |
The classic for loop is the one to use when you know how many times to repeat. Its parentheses hold three expressions, separated by semicolons:
for (exp1; exp2; exp3) {
// code block to be executed
}
| exp1 | Runs once, before the loop starts (usually let i = 0) |
| exp2 | Checked before every pass; the loop stops when it is false |
| exp3 | Runs after every pass (usually i++) |
for (let i = 0; i < 3; i++) {
console.log("The number is " + i);
}
while checks its condition before each pass, so it may run zero times. do...while checks after, so its body always runs at least once, even if the condition starts out false.
while or do...while loop only stops when something inside it makes the condition false. Forget the i++ and the condition stays true forever: the page freezes and the browser eventually offers to kill it. Before you run a loop, point to the line that moves it toward the finish.
In for...of and for...in, the loop variable can be declared with const, let, or var. const is the usual choice because each pass gets a brand-new variable. The classic for is different: it changes the same variable with i++, so it needs let:
for (const i = 0; i < 3; i++) { }
The body runs once with i as 0, then i++ tries to change a constant. Avoid var in loop headers: it leaks the variable outside the loop (see JS Scope).
const scores = [88, 92, 75];
for (const s of scores) { } // just the values: first choice
for (const [i, s] of scores.entries()) { } // index AND value
for (let i = scores.length - 1; i >= 0; i--) { } // backwards, or skipping around
scores.forEach((s, i) => { }); // fine when you never need to stop early
for (const [i, s] of scores.entries()) {
console.log(`#${i + 1}: ${s}`);
}
for (const ch of "Hi!") {
console.log(ch);
}
Plain objects cannot go straight into for...of. Use Object.entries to get [key, value] pairs, or for...in for keys:
const stock = { pens: 40, tape: 6, glue: 0 };
for (const [item, qty] of Object.entries(stock)) {
if (qty === 0) continue;
console.log(`${item}: ${qty}`);
}
Object.entries with for...of is usually clearer than for...in: you get the value directly instead of writing stock[key], and you only see the object's own properties.
for...in walks an object's enumerable properties: the keys you can see in a loop. On an array those keys are the indexes, but as strings: for (const k in ["a", "b"]) gives "0" and "1", so k + 1 is "01", not 1. Use for...of (or entries()) for arrays and keep for...in for objects.
A Set hands for...of its values. A Map hands it [key, value] pairs, which you can destructure right in the loop header:
const tags = new Set(["js", "css"]);
for (const tag of tags) {
console.log(tag);
}
const ages = new Map([["Ava", 16], ["Ben", 17]]);
for (const [name, age] of ages) {
console.log(`${name} is ${age}`);
}
let guesses = 0;
let n;
do {
n = Math.floor(Math.random() * 10);
guesses++;
} while (n !== 7);
console.log(`Rolled a 7 after ${guesses} tries`);
If the goal is to build something from an array, the array methods from Array Iterations usually say it more clearly than a loop:
// Loop version
const passing = [];
for (const s of scores) {
if (s >= 80) passing.push(s);
}
// Method version: same result, one line
const passing2 = scores.filter(s => s >= 80);
console.log(passing, passing2);
Array methods like forEach(), map(), filter(), and reduce() are often shorter and clearer than a loop for array work. The one thing they cannot do is stop early:
scores.forEach(s => {
if (s > 90) break; // not allowed here
});
break and continue only work inside a real loop, and the arrow function is not one. When you might need to stop, use for...of.
map / filter / reducefor...offor...of over Object.entries(obj)forwhileIn for (let i = 0; i < 5; i++), when does i++ run?
i < 5 is falseWhat does this log?
let n = 10;
do {
console.log(n);
} while (n < 5);
10. The condition is false from the start, but do...while runs its body once before checking. A plain while would log nothing.You need to search an array and stop as soon as you find a match. Which should you use?
forEachfor...infor...of with breakmapforEach and map cannot break (it is a SyntaxError), and for...in gives string indexes, not values. (find() from Array Search also works.)Object.entries and print only the ones below 70forEach so it can stop early when it finds a certain valuefor loop, for...of, and reduce