Iterating over objects is fundamental. Each method has different behavior with inheritance, enumerability, and performance.
Object.keys()
const user = { name: "Rahul", age: 30, role: "engineer" };
Object.keys(user); // ["name", "age", "role"]
// Only own enumerable string-keyed properties
// Does NOT include inherited properties
// Does NOT include Symbol propertiesfor...in
const parent = { inherited: true };
const child = Object.create(parent);
child.own = true;
for (const key in child) {
console.log(key); // "own", "inherited" — includes inherited!
}
// To filter own properties:
for (const key in child) {
if (child.hasOwnProperty(key)) {
console.log(key); // "own" only
}
}Object.entries()
const user = { name: "Rahul", age: 30 };
Object.entries(user); // [["name", "Rahul"], ["age", 30]]
// Perfect for destructuring
for (const [key, value] of Object.entries(user)) {
console.log(`${key}: ${value}`);
}
// Convert to Map
const map = new Map(Object.entries(user));Object.values()
const scores = { math: 95, science: 88, english: 92 };
const average = Object.values(scores).reduce((a, b) => a + b, 0) / Object.values(scores).length;Comparison Table
| Method | Own Props | Inherited | Symbols | Returns |
|---|---|---|---|---|
| Object.keys() | ✅ | ❌ | ❌ | string[] |
| Object.values() | ✅ | ❌ | ❌ | any[] |
| Object.entries() | ✅ | ❌ | ❌ | [string, any][] |
| for...in | ✅ | ✅ | ❌ | iterates keys |
| Object.getOwnPropertyNames() | ✅ | ❌ | ❌ | includes non-enumerable |
| Reflect.ownKeys() | ✅ | ❌ | ✅ | everything own |
Performance
For large objects (10K+ properties), for...in with hasOwnProperty is slightly faster than Object.keys().forEach() because it doesn't create an intermediate array. But for typical objects, the difference is negligible — choose readability.
Best Practices
- Default to
Object.entries()when you need both key and value - Use
Object.keys()when you only need keys - Avoid
for...inunless you specifically need inherited properties - Use
Reflect.ownKeys()when you need Symbol properties too