A complete guide to iterating over objects in JavaScript — covering for...in
, Object.keys
, Object.entries
, Object.values
, and Maps—without the common mistakes.

for...in
, Object.keys
, Object.entries
, Object.values
, and Maps—without the common mistakes.Introduction
Looping through arrays in JavaScript is straightforward:
for (const item of arr) { ... }
But with objects, things get messy. Should you use for...in
, Object.keys
, Object.entries
, or for...of
with a Map? Why does for...in
sometimes show weird inherited stuff? And why doesn’t for...of
even work on objects?
This guide explains the right ways to loop objects, the pitfalls that trip up developers, and the modern, safe patterns you should be using in real-world projects.
1. Why for...of
Doesn’t Work on Plain Objects
const user = { id: 1, name: "Ali" };
for (const x of user) {
// ❌ TypeError: user is not iterable
}
Plain objects aren’t iterable by default. Only arrays, Maps, Sets, and other iterable types work with for...of
.
👉 To loop objects, you need keys, values, or entries.
2. The Old-School Way: for...in
const user = { id: 1, name: "Ali" };
for (const key in user) {
console.log(key, user[key]);
}
⚠️ Pitfalls of for...in
:
- Loops over all enumerable properties, including inherited ones.
- If someone extends
Object.prototype
, those show up too.
Safer version:
for (const key in user) {
if (Object.hasOwn(user, key)) {
console.log(key, user[key]);
}
}
👉 Still, modern devs avoid for...in
unless necessary.
3. Modern Standard: Object.keys
const user = { id: 1, name: "Ali" };
Object.keys(user).forEach((key) => {
console.log(key, user[key]);
});
- Returns array of own enumerable string keys.
- Doesn’t include inherited props or symbols.
- Safe, predictable, and array-friendly.
4. Object.values
: Just the Values
const scores = { Ali: 90, Laiba: 85, Umar: 75 };
for (const value of Object.values(scores)) {
console.log(value);
}
👉 Perfect when you only care about values, not keys.
5. Object.entries
: Keys + Values
const settings = { darkMode: true, version: 2 };
for (const [key, value] of Object.entries(settings)) {
console.log(key, value);
}
- Clean destructuring.
- Pairs perfectly with
map
,filter
,reduce
. - Great for transformations:
const doubled = Object.fromEntries(
Object.entries({ a: 1, b: 2 }).map(([k, v]) => [k, v * 2])
);
6. When to Use Map
Instead of Object
Objects weren’t originally designed for key iteration — they’re records. If you need guaranteed iteration order, arbitrary key types, or frequent insert/delete, use a Map.
const map = new Map([
["id", 1],
["name", "Ali"]
]);
for (const [k, v] of map) {
console.log(k, v);
}
Benefits of Map
:
- Iteration order is insertion order.
- Keys can be anything (objects, functions).
- Direct
for...of
support.
7. Practical Use Cases
A) Logging API response safely
const response = { status: 200, message: "OK" };
for (const [k, v] of Object.entries(response)) {
console.log(`${k}: ${v}`);
}
B) Counting values
const votes = { A: 2, B: 3, C: 1 };
const total = Object.values(votes).reduce((sum, v) => sum + v, 0);
C) Transforming keys
const obj = { first_name: "Ali", last_name: "Rahmat" };
const camel = Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k.replace(/_./g, s => s[1].toUpperCase()), v])
);
8. TypeScript Tips
A) Iterating with type safety
type User = { id: number; name: string };
const user: User = { id: 1, name: "Ali" };
for (const [k, v] of Object.entries(user) as [keyof User, User[keyof User]][]) {
console.log(k, v);
}
B) Generic object iteration helper
function forEachEntry<T extends object>(
obj: T,
fn: (key: keyof T, value: T[keyof T]) => void
) {
Object.entries(obj).forEach(([k, v]) =>
fn(k as keyof T, v as T[keyof T])
);
}
9. Edge Cases & Gotchas
- Symbols aren’t included in
Object.keys
,values
, orentries
. UseObject.getOwnPropertySymbols
for those. - Non-enumerable properties don’t show up. Use
Object.getOwnPropertyNames
. - Order: For objects, property order is insertion order for string keys (in modern JS). Numeric-like keys (
"1"
,"2"
) come first in ascending order. - Inherited props: Only
for...in
includes them—avoid unless needed.
10. Performance Notes
for...in
+hasOwn
guard is slower thanObject.keys
.- For large objects, prefer
Object.keys
/entries
with plainfor
loops overforEach
for max speed. - For very dynamic data, consider using
Map
for direct iteration.
Conclusion
Looping over objects is deceptively tricky — but once you know the tools, you’ll never reach for for...in
blindly again.
- ✅ Use
Object.keys
/values
/entries
for predictable, modern iteration. - ✅ Use
Map
when you need iteration order, non-string keys, or frequent updates. - ⚠️ Avoid raw
for...in
unless you really want inherited props. - 🔒 Don’t forget about symbols and non-enumerables — they require separate APIs.
Pro Tip: Think of objects as records and Maps as collections. Pick the right tool for the iteration story you need.
Call to Action (CTA)
What’s your go-to way of looping over objects — Object.entries
pipelines, for...in
with guards, or Map
all the way?
Drop your pattern in the comments, and share this with a teammate who still does for...in
without a guard.
Leave a Reply