Question
Why is using for...in to iterate over arrays considered a bad practice in JavaScript? What problems can it cause, and which alternatives should be used when looping through array values?
Short Answer
By the end of this page, you will understand that for...in iterates over property names rather than reliably iterating over array elements. You will know when to use for...of, classic for loops, and array methods such as forEach, map, and filter instead.
Concept
for...in is designed to enumerate an object's enumerable property keys. An array is also an object in JavaScript, so for...in can list more than its numeric indexes.
For example, an array normally has keys such as "0", "1", and "2". However, it can also have custom properties, and it may inherit enumerable properties from Array.prototype or another prototype. A for...in loop may include those properties too.
const colors = ["red", "green", "blue"];
for (const key in colors) {
console.log(key);
}
// "0"
// "1"
// "2"
Notice that the loop variable contains keys as strings, not the values "red", "green", and "blue".
This matters because array iteration usually means processing values in order. for...in does not communicate that intent well, can include unexpected properties, and should not be relied on for array ordering. Use an array-focused construct instead.
Mental Model
Think of an array as a numbered shelf of books:
for...ofsays: “Give me each book on the shelf.”- A classic
forloop says: “Visit shelf position 0, then 1, then 2.” for...insays: “Tell me every label attached to this shelf object.”
The labels include the expected position labels ("0", "1") but might also include extra labels someone attached later. That is useful when inspecting an object's properties, but it is not a safe way to read the books themselves.
Syntax and Examples
Use for...of when you need array values.
const fruits = ["apple", "banana", "orange"];
for (const fruit of fruits) {
console.log(fruit);
}
// apple
// banana
// orange
Use entries() when you need both the index and value.
const fruits = ["apple", "banana", "orange"];
for (const [index, fruit] of fruits.entries()) {
console.log(`${index}: ${fruit}`);
}
// 0: apple
// 1: banana
// 2: orange
A for...in loop produces property names instead:
const fruits = [, , ];
( key fruits) {
.(key, fruits[key]);
}
Step by Step Execution
Consider this code:
const tasks = ["email client", "write report"];
tasks.priority = "high";
for (const key in tasks) {
console.log(key, tasks[key]);
}
Step by step:
tasksis an array with elements at indexes0and1.tasks.priority = "high"adds a normal object property to the array.for...inlooks for enumerable property keys ontasks.- It can visit the keys
"0","1", and"priority". - The output can therefore be:
0 email client
1 write report
priority high
The priority property is not an array element, but for...in treats it as another enumerable property. By contrast, only iterates the array's iterable values:
Real World Use Cases
Choose the loop based on the work you need to do:
- Render a list in a command-line script: use
for...ofto process each filename, user, or product. - Validate API data: use
for...ofto inspect every item in an array returned by an API. - Create transformed data: use
map()to turn product prices into formatted display strings. - Remove unwanted records: use
filter()to keep only active users. - Stop after finding a match: use
for...ofwithbreak, orfind()when you only need the matching value. - Inspect object configuration: use
for...inonly when you intentionally want an object's enumerable property keys, usually with an own-property check.
const prices = [12, 25, 8];
const formattedPrices = prices.map(price => `$${price.toFixed(2)}`);
console.log(formattedPrices);
// ["$12.00", "$25.00", "$8.00"]
Real Codebase Usage
In production code, developers make the operation's intent clear.
Process every value
for (const user of users) {
sendWelcomeEmail(user);
}
Transform without changing the original array
const userNames = users.map(user => user.name);
Filter invalid values before continuing
const validOrders = orders.filter(order => order.total > 0);
Use a guard clause in a loop
for (const order of orders) {
if (!order.isPaid) {
continue;
}
shipOrder(order);
}
Iterate object keys intentionally
For plain objects, Object.keys() makes it clear that you want own keys only:
Common Mistakes
Treating for...in values as array elements
for...in returns keys, so this code logs indexes rather than names:
const names = ["Ada", "Lin"];
for (const name in names) {
console.log(name);
}
// 0
// 1
Use for...of:
for (const name of names) {
console.log(name);
}
Assuming only indexes will be visited
const items = ["pen", "notebook"];
items.category = "stationery";
for (const key in items) {
console.log(key);
}
The loop can include "category". Do not use when processing array elements.
Comparisons
| Option | Iterates over | Best use | Important note |
|---|---|---|---|
for...in | Enumerable property keys | Object-property enumeration | Not recommended for arrays; keys are strings and extras may appear. |
for...of | Iterable values | Reading array values in sequence | Supports break, continue, and await in an async function. |
Classic for | Numeric index you control | Index-based logic or maximum control | Useful when comparing nearby elements or changing indexes. |
array.forEach() | Array values |
Cheat Sheet
// Preferred: values
for (const value of array) {
// use value
}
// Preferred: index and value
for (const [index, value] of array.entries()) {
// use index and value
}
// Preferred: controlled index loop
for (let index = 0; index < array.length; index++) {
// use array[index]
}
// Transform values into a new array
const result = array.map(value => transform(value));
// Keep matching values
const result = array.filter(value => matches(value));
// Iterate a plain object's own keys
for (const key of Object.keys(object)) {
// use object[key]
}
Rules to remember:
- Do not use
for...infor normal array iteration. for...inyields property names, not values.
FAQ
Why does for...in return strings for array indexes?
JavaScript object property names are strings (apart from symbols). Array indexes are represented as property keys, so for...in produces values such as "0" and "1".
Does for...in always fail with arrays?
No. It often appears to work for a simple array with no extra properties. The problem is that it has the wrong semantics and can produce unexpected keys as code changes.
Should I use for...of or forEach() for arrays?
Use for...of when you may need break, continue, or await. Use forEach() for a short callback that performs a side effect for every item. Use map() when creating a new array.
Can for...of give me the index too?
Yes. Use array.entries():
( [index, value] array.()) {
.(index, value);
}
Mini Project
Description
Build a small task-reporting script that loops through a list of tasks safely. The script demonstrates why task metadata should be stored separately rather than attached directly to an array, and why for...of is the correct tool for processing array values.
Goal
Display each task, count completed tasks, and show project metadata without accidentally treating metadata as a task.
Requirements
- Create an array containing task objects with
titleandcompletedproperties. - Store project metadata in a separate object.
- Use
for...ofto display every task and count completed tasks. - Print a final summary containing the project name and completed-task count.
- Do not use
for...into iterate over the tasks array.
Keep learning
Related questions
Abort Ajax Requests with jQuery jqXHR.abort()
Learn how to cancel an in-progress jQuery Ajax request with jqXHR.abort(), handle abort status safely, and avoid stale UI updates.
Access the Correct this Inside a JavaScript Callback
Learn why JavaScript this changes in callbacks and how to preserve an object context using bind, arrow functions, and event handler patterns.
Add Key-Value Pairs to JavaScript Objects
Learn how to add key-value pairs to JavaScript objects with dot and bracket notation, dynamic keys, examples, and common mistakes.