In short
Learn the best ways to loop over arrays and objects in JavaScript with for...of, Object.keys, Object.values and Object.entries, plus optional chaining for safe access. How to loop over an array with for...of The for...of loop goes through each value of an array, one by one, without a counter.
Learn the best ways to loop over arrays and objects in JavaScript with for...of, Object.keys, Object.values and Object.entries, plus optional chaining for safe access.
How to loop over an array with for...of
The for...of loop goes through each value of an array, one by one, without a counter. It reads almost like English: "for each item of the list, do this".
const songs = ["Intro", "Sunrise", "Night Drive"];
for (const song of songs) {
console.log(`Now playing: ${song}`);
}
// Output:
// Now playing: Intro
// Now playing: Sunrise
// Now playing: Night Drive
for (const letter of "Hey") {
console.log(letter);
}
// Output:
// H
// e
// y
Each time through the loop, song holds the next value. You can use const because a fresh variable is created for every round. for...of also works on strings, giving you one character at a time.
Unlike forEach, you can stop a for...of loop early with break, or skip a round with continue.
If you also need the index, use the entries() method of arrays together with destructuring:
const steps = ["Preheat oven", "Mix flour", "Bake 20 min"];
for (const [index, step] of steps.entries()) {
console.log(`Step ${index + 1}: ${step}`);
}
// Output:
// Step 1: Preheat oven
// Step 2: Mix flour
// Step 3: Bake 20 min
steps.entries() gives pairs like [0, "Preheat oven"], and [index, step] unpacks each pair.
Why you should not use for...in on arrays
JavaScript also has a for...in loop. It loops over the keys (property names) of an object, not the values. On an array, those keys are index strings like "0", "1", "2".
const colors = ["red", "green"];
for (const key in colors) {
console.log(key, typeof key);
}
// Output:
// 0 string
// 1 string
This causes problems: the index is a string, so key + 1 gives "01", not 1. for...in can also pick up extra properties that were added to the array. For arrays, always use for...of, a classic for loop, or array methods like forEach.
Common mistake: Mixing up
ofandin. Remember:for...ofgives you the values of an array.for...ingives you the keys in an object. For objects, theObjectmethods below are usually clearer anyway.
How to loop over an object with Object.keys, Object.values and Object.entries
Plain objects do not work with for...of directly. Instead, you turn the object into an array first, using one of three built-in helpers:
| Method | Returns | Example result |
|---|---|---|
Object.keys(obj) | Array of keys | ["html", "css"] |
Object.values(obj) | Array of values | [90, 75] |
Object.entries(obj) | Array of [key, value] pairs | [["html", 90], ["css", 75]] |
Because these return arrays, you can loop over them with for...of or use any array method.
const scores = { html: 90, css: 75, javascript: 82 };
console.log(Object.keys(scores));
// Output: ["html", "css", "javascript"]
const total = Object.values(scores).reduce((sum, s) => sum + s, 0);
console.log(`Average: ${total / Object.keys(scores).length}`);
// Output: Average: 82.33333333333333
for (const [subject, score] of Object.entries(scores)) {
const result = score >= 80 ? "pass with merit" : "pass";
console.log(`${subject}: ${score} (${result})`);
}
// Output:
// html: 90 (pass with merit)
// css: 75 (pass)
// javascript: 82 (pass with merit)
The last loop is the pattern you will use most: Object.entries gives pairs, and [subject, score] destructures each pair into two clear variable names.
Tip: To round the average for display, use
(total / 3).toFixed(1), which gives the string"82.3".
Looping over an array of objects
Real data often comes as an array of objects. Combine for...of with object destructuring to keep the loop short:
const team = [
{ name: "Ada", role: "Developer" },
{ name: "Ben", role: "Designer" },
];
for (const { name, role } of team) {
console.log(`${name} works as a ${role}.`);
}
// Output:
// Ada works as a Developer.
// Ben works as a Designer.
Optional chaining: safe access with ?.
When data is nested, some parts may be missing. Reading a property of undefined throws an error and stops your script. Optional chaining ?. checks first: if the value on the left is null or undefined, it stops and returns undefined instead of crashing.
const users = [
{ name: "Mo", address: { city: "Cairo" } },
{ name: "Lea" },
];
for (const user of users) {
// user.address.city would crash for Lea
const city = user.address?.city ?? "Unknown city";
console.log(`${user.name}: ${city}`);
}
// Output:
// Mo: Cairo
// Lea: Unknown city
user.address?.city returns undefined for Lea because she has no address. The ?? operator (called nullish coalescing) then gives a fallback value when the left side is null or undefined. You can also use ?. with methods, like user.getName?.(), and with brackets, like order.items?.[0].
Note: Use
?.only where data is truly allowed to be missing. If a value should always exist, a crash helps you find the bug faster. See MDN on optional chaining for more.
Practice exercise
Create an object inventory where keys are fruit names and values are quantities, such as { apples: 12, bananas: 0, kiwis: 5 }.
- Use
Object.entriesandfor...ofto log lines like "apples: 12 in stock". - Log "Sold out!" instead for any fruit with quantity
0. - Use
Object.valuesto log the total number of fruits. - Write a line using
?.and??that safely readsinventory.extras?.bagsand falls back to0.
Hint: Destructure with const [fruit, qty] of Object.entries(inventory) and use an if for the sold-out case.
Key takeaways
for...ofloops over values of arrays and strings, and supportsbreakandcontinue.- Avoid
for...inon arrays; its keys are strings and it can include extra properties. Object.keys,Object.valuesandObject.entriesturn objects into arrays you can loop over.- Destructuring inside loops (
[key, value]or{ name, role }) keeps code readable. - Optional chaining
?.safely reads nested data, and??provides a fallback value.