What are the three parts of a for loop?
A classic for loop has three parts in its brackets, separated by semicolons. The start, usually let i = 0, runs once before anything else. The check, such as i < prices.length, runs before every pass; while it is true, the body runs. The step, usually i++, runs after every pass.
Read for (let i = 0; i < 4; i++) as: start i at 0; while i is less than 4, run the body; after each pass add 1 to i. The body runs with i equal to 0, 1, 2 and 3, four times. When i reaches 4 the check fails and the loop ends.
Use let for the counter, never const, because the step changes it. Each pass gets its own copy of i, which you will rely on later when you create click handlers inside a loop.
Open the Step lens on the demo. The arrow moves from the check to the body to the step and back, and the variable panel shows i and total changing on each pass. Watching one loop this way teaches more than reading about ten.
const birds = ["wren", "robin", "jay"];
for (let i = 0; i < birds.length; i++) {
console.log(`${i + 1}. ${birds[i]}`);
}

for loop's brackets run at different moments. The start runs once, the check runs before every pass, and the step runs after every pass. The loop ends the first time the check is false.When should you use for...of instead?
Most loops in real code walk through every item of an array, in order. for...of does exactly that with less to go wrong: for (const bird of birds) gives you each bird in turn, and there is no index, no length and no step to get wrong.
Use const for the loop variable when you do not reassign it inside the body. A new bird is created for each pass, so const works.
for...of also works on strings, giving you one character at a time, and on other collections such as the list querySelectorAll returns. It does not work on plain objects; use Object.entries() for those, as the objects lesson shows.
Use a counting for loop when you need the index for something other than reading the item, when you want to step by 2, or when you loop backwards. If you need both the item and its position in a for...of, for (const [i, bird] of birds.entries()) gives you both.
const word = "finch";
for (const letter of word) {
console.log(letter.toUpperCase());
}How does a while loop differ?
A while loop has only a check. It runs the body as long as the condition stays true, and you manage everything else yourself. Use it when you do not know in advance how many passes you need: keep halving a number until it is small, keep reading until there is no more data, keep retrying until it works.
The responsibility is yours. Something in the body has to change what the condition tests. If it does not, the loop never ends.
There is also do...while, which runs the body once before checking. You will rarely need it.
let dough = 1000;
let folds = 0;
while (dough > 100) {
dough = dough / 2;
folds++;
}
console.log(`${folds} folds, ${dough}g left`);What do break and continue do?
break ends the loop immediately. Execution jumps to the first line after the loop. Use it when you have found what you were looking for and the remaining items do not matter.
continue ends only the current pass and moves to the next one. In a for loop the step still runs. Use it to skip items that do not apply, such as empty entries or sold-out products.
Mixing them up gives results that look plausible and are wrong. A loop meant to skip phone calls that uses break stops counting at the first call. The Step lens makes this obvious: watch where the arrow goes after the if.
Neither works inside forEach, the array method you will meet later. That is one reason to prefer for...of when you may need to stop early.
const shelf = ["rye", "", "spelt", "SOLD OUT", "oat"];
for (const loaf of shelf) {
if (loaf === "") {
continue;
}
if (loaf === "SOLD OUT") {
break;
}
console.log(loaf);
}Why does the last item go missing, or turn into undefined?
Off-by-one errors come from mixing up counting from 1 and counting from 0. An array of 4 items has indexes 0 to 3. Its length is 4, but there is no prices[4].
Writing i <= prices.length runs one pass too many, and the extra pass reads undefined. Added to a number, that gives NaN, which is exactly what the demo shows. Starting at i = 1 runs one pass too few and silently skips the first item.
The safe pattern is let i = 0; i < list.length. Memorise that shape and be suspicious of any <= next to length.
When a total looks wrong, log the index and the value inside the loop, or step through it. If the last line shows an index equal to the length, or a value of undefined, you have found it.
const prices = [3, 4.5, 2, 6];
for (let i = 0; i < prices.length; i++) {
console.log(i, prices[i]);
}What happens when a loop never stops?
An infinite loop runs forever because its condition never becomes false. Typical causes are forgetting i++, changing the wrong variable, or a step that moves away from the end, like i-- with i < 10.
In a normal browser tab, an endless loop freezes the page until the browser offers to kill it. The PlainFinch editor watches for this and stops any loop that runs longer than 1.5 seconds, then explains in the Console what happened. Your work is safe; fix the condition and run again.
Before writing a while, say out loud what makes it stop and point to the line that moves toward that. If you cannot point to one, the loop will not end.
let tries = 0;
while (tries < 3) {
console.log(`Attempt ${tries + 1}`);
tries++;
}How do you build up a total or a new list?
Most loops collect something. The pattern is always the same: create the empty result before the loop, update it inside the loop, and use it after the loop.
For a total, start with let total = 0; and add each value with total += price;. For a count, start at 0 and add 1 when an item matches. For a new list, start with const result = []; and call result.push(item) for the items you want. The array is const because the variable keeps pointing at the same array, even though its contents grow.
Declaring the result inside the loop is a common slip. Each pass then creates a fresh 0 or a fresh empty array, and the result never grows past one item.
For text, you can build an array of pieces and join it at the end. That is easier to get right than adding strings to a variable and remembering the separators.
const orders = ["rye", "oat", "rye", "spelt", "rye"];
let ryeCount = 0;
const others = [];
for (const loaf of orders) {
if (loaf === "rye") {
ryeCount++;
} else {
others.push(loaf);
}
}
console.log(ryeCount, others.join(", "));When are array methods nicer than a loop?
Arrays come with methods that wrap common loops: map makes a new list from each item, filter keeps the items that pass a test, find returns the first match, and reduce combines everything into one value. A loop that only does one of those jobs is often shorter as the method.
You will learn them in the array methods lesson. Learn loops first, because every method is a loop underneath, and because a plain loop is still the clearest choice when you need break, several results at once, or an await inside.
AI tools often produce long chains of methods, or a forEach that pushes into an outer array. When reading them, translate back to a loop in your head: what starts empty, what happens to each item, and what comes out at the end.
const prices = [3, 4.5, 2, 6];
const cheap = prices.filter((price) => price < 4);
console.log(cheap);Field notes
- A
forloop has a start, a check and a step, separated by semicolons. for...ofwalks through each item of an array with less to go wrong.- An array of 4 items has indexes 0 to 3, so
i <= lengthreads one too far. - Create the result before the loop, update it inside, and use it after.
Easy to get wrong
The last index is one less than the length. <= runs an extra pass that reads undefined.
A total declared inside the loop starts again at 0 on every pass. Create it before the loop and read it after.
The step changes the counter, so it must be let. With const the first i++ throws a TypeError.
Questions people ask
Which loop should I reach for first?
for...of for going through every item of an array or string. A counting for when you need the index or an unusual step. while when the number of passes depends on something you discover as you go.
What is for...in, and should I use it?
for...in loops over the keys of an object, as strings. It is easy to confuse with for...of, and on arrays it gives indexes as text. Prefer for...of with Object.keys() or Object.entries().
Why does my loop variable show the last value in every click handler?
That happens with var, which shares one variable across all passes. With let or const, each pass gets its own copy, so handlers created in the loop remember the right value.
Is a loop slow for big lists?
Looping over thousands of items takes a few milliseconds. What is slow is touching the page inside the loop many times. Build the content first, then add it to the page once.
How do I loop backwards?
Start at the last index and count down: for (let i = list.length - 1; i >= 0; i--). Note the - 1 and the >=; both are needed to include the first item.