JavaScript for...of vs for...in: Differences and Use Cases
JavaScript has two loop forms that look similar but solve different problems: for...of and for...in. This article explains what each loop does, how they behave on arrays, objects, strings, and other iterable values, and how to choose the right one without getting subtle bugs.
Quick answer: Use for...of to loop over values in an iterable such as an array, string, set, or map. Use for...in to loop over enumerable property names on an object, especially when you need keys rather than values.
Difficulty: Beginner
You'll understand this better if you know: basic JavaScript variables, arrays and objects, and how for loops work at a simple level.
1. What Are for...of and for...in?
for...of and for...in are both loop statements, but they iterate over different things.
- for...of gives you each value from an iterable.
- for...in gives you each enumerable property name, usually a key or index.
- for...of works with arrays, strings, maps, sets, and other iterable objects.
- for...in is mainly for objects when you need to inspect keys.
In practice, beginners often mix them up because both use the same loop shape. The important difference is what each loop returns to you.
2. Why This Comparison Matters
Choosing the wrong loop can lead to confusing output, accidental traversal of inherited properties, or code that works in one case but fails in another.
for...of is usually the better choice for arrays because it is simpler and reads like “give me each item.” for...in is useful when you need object keys, but it is not a general-purpose array loop.
Knowing the difference also helps you avoid common mistakes such as looping over array indexes when you wanted values, or trying to use for...of on a plain object.
3. Basic Syntax or Core Idea
for...of syntax
for...of reads values from an iterable one at a time. The loop variable receives the current value directly.
const numbers = [10, 20, 30];
for (const number of numbers) {
console.log(number);
}Here, number is each array value: 10, then 20, then 30.
for...in syntax
for...in loops over property names. On arrays, those property names are usually indexes written as strings.
const user = {
name: "Ava",
role: "admin"
};
for (const key in user) {
console.log(key, user[key]);
}Here, key becomes name and role, not the values Ava and admin.
4. Step-by-Step Examples
Example 1: Looping over an array with for...of
When you want the items in an array, for...of is usually the cleanest option.
const fruits = ["apple", "banana", "cherry"];
for (const fruit of fruits) {
console.log(fruit);
}This prints each fruit value directly. You do not need to manually access indexes.
Example 2: Looping over array indexes with for...in
for...in can loop over array indexes, but that is usually not the best choice unless you need the index as a property name.
const fruits = ["apple", "banana", "cherry"];
for (const index in fruits) {
console.log(index, fruits[index]);
}This works, but the loop variable is a string index like "0", not the value itself. For arrays, for...of is usually easier to read.
Example 3: Looping over object properties with for...in
Plain objects do not implement iteration in the same way arrays do, so for...in is a natural fit when you need keys.
const profile = {
name: "Mina",
country: "Canada",
active: true
};
for (const key in profile) {
console.log(key + ":", profile[key]);
}This is useful for printing key-value pairs, validating object fields, or building summaries.
Example 4: Looping over a string with for...of
Strings are iterable, so for...of can read each character directly.
const word = "code";
for (const character of word) {
console.log(character);
}This prints c, o, d, and e. The same loop is much less natural with for...in because you would get string indexes instead of letters.
5. Practical Use Cases
- Use for...of when processing array items such as shopping cart products, search results, or API response lists.
- Use for...of to walk through a Map or Set when you want values or entries.
- Use for...in when inspecting object fields such as form data, settings, or configuration objects.
- Use for...in when you need property names to build dynamic labels or compare keys.
- Use for...of for text processing tasks where each character matters.
6. Common Mistakes
Mistake 1: Using for...of on a plain object
for...of only works on iterable values. A plain object is not iterable by default, so the loop fails.
Problem: This code tries to iterate over an object with for...of, which causes a TypeError: object is not iterable.
const user = { name: "Ava", role: "admin" };
for (const entry of user) {
console.log(entry);
}Fix: Use for...in for keys, or convert the object to an array with Object.entries() if you want key-value pairs.
const user = { name: "Ava", role: "admin" };
for (const key in user) {
console.log(key, user[key]);
}
for (const [key, value] of Object.entries(user)) {
console.log(key, value);
}The corrected version works because the loop matches the data structure being used.
Mistake 2: Expecting for...in to give array values
Many beginners use for...in on arrays and then wonder why they get indexes instead of the items themselves.
Problem: This code logs array indexes like 0 and 1, not the actual values.
const numbers = [5, 10, 15];
for (const item in numbers) {
console.log(item);
}Fix: Use for...of when you need the array values.
const numbers = [5, 10, 15];
for (const item of numbers) {
console.log(item);
}The fixed version is clearer and avoids treating array indexes like data values.
Mistake 3: Forgetting that for...in can see inherited properties
for...in walks enumerable properties, including ones inherited through the prototype chain. That can be surprising if you only want an object’s own properties.
Problem: This code may print inherited properties if they are enumerable, which can produce unexpected output or duplicate work.
const base = { shared: "yes" };
const child = Object.create(base);
child.own = "value";
for (const key in child) {
console.log(key);
}Fix: Filter with Object.hasOwn() when you only want properties that belong directly to the object.
const base = { shared: "yes" };
const child = Object.create(base);
child.own = "value";
for (const key in child) {
if (Object.hasOwn(child, key)) {
console.log(key);
}
}The corrected version avoids accidental traversal of inherited properties.
7. Best Practices
Prefer for...of for arrays and other iterables
When you want values, for...of is easier to read and less error-prone than looping by index or using for...in.
const names = ["Lia", "Noah", "Omar"];
for (const name of names) {
console.log(name);
}This style directly communicates that the loop is about values, not indexes.
Use for...in only when you actually need keys
If you need to inspect property names or build a dynamic lookup, for...in is appropriate. Otherwise, consider Object.keys() or Object.entries() for clearer intent.
const settings = { theme: "dark", fontSize: 16 };
for (const key in settings) {
if (Object.hasOwn(settings, key)) {
console.log(key, settings[key]);
}
}Adding the ownership check keeps the loop focused on the object’s own data.
Remember that array indexes from for...in are strings
If you use for...in on an array, the loop variable is a string key. That can matter when you compare or format values.
const letters = ["a", "b", "c"];
for (const index in letters) {
console.log(Number(index) + 1, letters[index]);
}This makes the string nature of the index explicit, but for arrays you still usually want for...of instead.
8. Limitations and Edge Cases
- for...of requires an iterable, so plain objects do not work unless converted with Object.keys(), Object.values(), or Object.entries().
- for...in can enumerate inherited enumerable properties, which may surprise you when using objects created with prototypes.
- for...in on arrays does not guarantee numeric ordering in the way many developers expect for data processing tasks; it is not meant as an array-value loop.
- Sparse arrays can behave differently from dense arrays, and for...in may skip holes because holes are not real properties.
- Strings with surrogate pairs or certain Unicode characters may not behave the way you expect if you assume each visible symbol is one code unit; for...of iterates string code points more naturally than index-based loops.
- Neither loop is ideal for asynchronous work by itself; if you need controlled async flow, consider for...of with await inside an async function rather than using forEach.
9. Practical Mini Project
Here is a small utility that prints a summary of a grocery list object and its items. It uses for...in for object keys and for...of for array values.
const shoppingList = {
fruits: ["apple", "banana"],
vegetables: ["carrot", "spinach"],
drinks: ["water", "tea"]
};
for (const category in shoppingList) {
if (Object.hasOwn(shoppingList, category)) {
console.log(category + ":");
for (const item of shoppingList[category]) {
console.log("- " + item);
}
}
}This example shows the practical division of labor: for...in identifies which category keys exist, and for...of processes the items inside each category.
10. Key Points
- for...of loops over values from an iterable.
- for...in loops over enumerable property names.
- Use for...of for arrays, strings, sets, and maps when you want values.
- Use for...in for objects when you want keys, and filter with Object.hasOwn() if needed.
- Do not use for...in as a default array loop.
- Do not use for...of on plain objects unless you convert them first.
11. Practice Exercise
Try this small exercise to make the difference stick.
- Create an array of three city names and print each city with for...of.
- Create an object with three properties and print each key with for...in.
- Then print each object value by combining for...in with bracket notation.
Expected output: You should see each city name on its own line, followed by each object key and its corresponding value.
Hint: Remember that for...of gives values directly, while for...in gives keys that you can use to access values.
Solution:
const cities = ["Paris", "Tokyo", "Lima"];
const person = {
firstName: "Sam",
lastName: "Lee",
age: 29
};
for (const city of cities) {
console.log(city);
}
for (const key in person) {
if (Object.hasOwn(person, key)) {
console.log(key + ":", person[key]);
}
}This solution works because each loop matches the structure it is meant to handle.
12. Final Summary
for...of and for...in are easy to confuse, but they answer different questions. for...of asks, “What are the values in this iterable?” while for...in asks, “What enumerable keys does this object have?”
For most array and string work, reach for for...of. For object-key inspection, use for...in carefully and remember to guard against inherited properties when necessary.
If you want to go further, next learn Object.keys(), Object.entries(), and how iterators make for...of possible.