Q1When you loop over orderIds.entries() with for...of, what is index on the first pass?
Loops — for, for...of, and for...in
Learn JavaScript loops from the ground up: for for counting, for...of for stepping through elements, for...in for keys, and how to build nested loops.
To print the price of each ordered item on its own line, you'd have to write the same line 3 times for 3 items and 10 times for 10. The number of items depends on the data, so you can't know in advance how many lines you'll need.
This article covers for, which repeats by counting, for...of, which hands you elements one at a time, and for...in, which walks through keys.
Repeating Code Once per Item — The Three Expressions of for
Say you want to print the item names from an order, one per line. If you write them out one by one, starting with console.log(orderLines[0]), you'll have to add a line as soon as the order grows to 4 items, and edit the code every time the count changes.
A for loop (a statement that repeats the same code while counting) takes three expressions in its parentheses, separated by semicolons, and the code you want to repeat goes inside the curly braces. What's inside those braces is called the body. From left to right, the three expressions are the initialization, the condition, and the update: the condition is checked before entering the body, and the update runs after the body.
i++ means the same thing as i = i + 1. Since its value changes on every pass, i is declared with let.
const orderLines = ["Mug", "Notebook", "Ballpoint pen"]; // item names in the order
// Start i at 0 and add 1 with i++ until it reaches the item count
for (let i = 0; i < orderLines.length; i++) {
// Indexes start at 0, so display i + 1 to count from item 1
console.log(`Item ${i + 1}: ${orderLines[i]}`);
}
// Item 1: Mug
// Item 2: Notebook
// Item 3: Ballpoint pen
console.log(`Items shown: ${orderLines.length}`); // Items shown: 3
The initialization runs only once at the very start, the condition runs before the body, and the update runs after the body. On the pass where the condition becomes false, the loop ends without running the body.
Checking the condition, running the body, and running the update together make up one pass (an iteration). Because the update runs after the body, i always equals the number of times the body has already finished, so on the first pass the body runs with i still at 0.
The table below shows i and the condition check on each pass.
| Pass | Value of i and the condition | What happens in the body |
|---|---|---|
| Pass 1 | i = 0 / 0 < 3 is true | Item 1: Mug |
| Pass 2 | i = 1 / 1 < 3 is true | Item 2: Notebook |
| Pass 3 | i = 2 / 2 < 3 is true | Item 3: Ballpoint pen |
| Start of pass 4 | i = 3 / 3 < 3 is false | The body doesn't run; the loop ends |
Reading Past the End of an Array Gives undefined
If you write the condition as i <= orderLines.length, you get one extra pass at the end, which reads orderLines[3] — an element that doesn't exist. Anything out of range is undefined, so you'll see "Item 4: undefined," and if the loop is adding up prices, the result becomes NaN. Keep the loop running only while the index is less than the item count.
Getting Each Element Directly — for...of and entries
Say you want to process a list of order numbers waiting to ship, one after another. With for, you'd declare an index variable, write the condition and the update, and then read each one with orderIds[i] — and a typo in the index means reading the wrong element.
for...of (a statement that takes the elements of an array or other iterable one at a time and puts each into a variable) is written as for (const orderId of orderIds), with only the declaration and the array inside the parentheses.
When you need the position too, loop over orderIds.entries(). On each pass it gives you a pair like [0, "A-1001"], which you unpack with square brackets as [index, orderId].
const orderIds = ["A-1001", "A-1002", "A-1003"]; // order numbers waiting to ship
// orderId holds the element itself (no index in sight)
for (const orderId of orderIds) {
console.log(`Awaiting shipment: ${orderId}`);
}
// Awaiting shipment: A-1001 … (3 lines)
// When you also need the index, get pairs from entries()
for (const [index, orderId] of orderIds.entries()) {
console.log(`#${index + 1}: ${orderId}`);
}
// #1: A-1001
// #2: A-1002
// #3: A-1003
| How you receive it | What goes into the variable | Printed line |
|---|---|---|
| Get elements with of | The element itself (A-1001) | Awaiting shipment: A-1001 |
| Unpack entries() into [index, orderId] | index is 0 / orderId is A-1001 | #1: A-1001 |
| Receive entries() in a single variable | The whole index-and-element pair | Still the array [0, "A-1001"] |
const [index, orderId] works the same way as unpacking arrays in the destructuring article: the index goes into the first position and the element into the second. A new variable is created on every pass, so you can declare a for...of loop variable with const without getting a reassignment error.
Walking Through Keys — for...in, and Why Not to Use It on Arrays
Sometimes you want to print both the key names and the values side by side, as with a member profile. You could turn the keys into an array with Object.keys(profile) and loop over it with for...of, but if all you need is to walk through the keys, there's a shorter way.
for...in (a statement that goes through an object's keys one at a time) is written as for (const key in profile). The variable holds the key as a string, not the value, so you read the value with bracket notation, as in profile[key].
const profile = { displayName: "Alice", plan: "premium", posts: 12 };
// key holds the key as a string (not the value)
for (const key in profile) {
console.log(`${key}: ${profile[key]}`);
}
// displayName: Alice
// plan: premium
// posts: 12
// On an array, the indexes come through as strings
const stockCounts = [5, 0, 12];
for (const key in stockCounts) {
console.log(`${typeof key} / ${key + 1}`);
}
// string / 01
// string / 11
// string / 21 ← joined as strings, not added as numbers
If you're only reading elements, as in stockCounts[key], the string "1" still gets you the value. You could convert it with Number(key) to use it in calculations, but for array elements it's simpler to choose a different loop from the start. The table below shows when to use each of the three.
| Syntax | What goes into the variable | Best for |
|---|---|---|
| for (let i = 0; i < count; i++) | The index as a number | Using the index in calculations |
| for (const item of array) | The element itself | Processing each element in order |
| for (const key in object) | The key as a string | Walking through an object's keys |
You Can't Loop Over an Object with for...of
If you put an object to the right of of, as in for (const item of profile), it stops with TypeError: profile is not iterable. Plain objects aren't iterable: they don't define a way to hand over their contents one at a time. To go through the values too, turn the object into an array with Object.entries first.
Listing Every Combination — Inner and Outer Loops
Say you want to list which products each store carries, as every store-and-product combination. A single loop can step through either the stores or the products, but not both at once. And both the number of stores and the number of products depend on the data.
You can put another loop directly inside a loop's body. The inner loop runs all the way through during each pass of the outer loop, so the number of times the inner body runs is the outer count multiplied by the inner count. The inner loop can also read the outer loop's variable directly.
const stores = ["Shibuya", "Yokohama"];
const products = ["Mug", "Notebook"];
// During each pass of the outer loop, the inner loop runs all the way through
for (const store of stores) {
for (const product of products) {
console.log(`${store} / ${product}`);
}
}
// Shibuya / Mug
// Shibuya / Notebook
// Yokohama / Mug
// Yokohama / Notebook
store— changes from "Shibuya" to "Yokohama" on each pass- The outer body runs only 2 times
product— changes from "Mug" to "Notebook" on each inner pass- Can read the outer
storedirectly - This body runs 2 × 2 = 4 times
product and the outer store. product, declared in the inner loop, can only be used inside the inner curly braces.The inner for...of starts again from the beginning of products every time the outer pass changes, so while store stays the same, only product changes. A variable declared at the top of the outer body is also created fresh on each outer pass. How curly braces limit where a variable can be used is covered in the article on scope.
Knowledge Check
Answer each question one by one.
Q2When you loop over an array with for...in, what goes into the loop variable?
Q3If you loop over a 3-element array and a 4-element array in a nested loop, how many times does the inner body run?