Object.keys, values, entries.
Three methods, three outputs.
Objects don't have a forEach like arrays do. To iterate them, you convert them into arrays first — with Object.keys, Object.values, or Object.entries. Each returns a different shape: property names, property values, or [key, value] pairs. This page covers when to use each, the difference from for...in, and the iteration patterns that come up in real code.
Short answer: Object.keys(obj) → array of names. Object.values(obj) → array of values. Object.entries(obj) → array of [key, value] pairs. All three iterate own enumerable properties only — no inherited ones. Use entries for iteration (you usually need both), keys for checking existence, values for aggregation. Avoid for...in — it includes inherited properties.
✅ The three methods
Same object, three shapes.
const user = {
name: 'Alice',
age: 30,
email: 'alice@example.com'
};
// Object.keys — array of names
console.log(Object.keys(user));
// ['name', 'age', 'email']
// Object.values — array of values
console.log(Object.values(user));
// ['Alice', 30, 'alice@example.com']
// Object.entries — array of [key, value] pairs
console.log(Object.entries(user));
// [['name', 'Alice'], ['age', 30], ['email', 'alice@example.com']]
// Iterate with entries
Object.entries(user).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
// name: Alice
// age: 30
// email: alice@example.com
// Iterate with for...of
for (const [key, value] of Object.entries(user)) {
console.log(`${key} = ${value}`);
}
Shape of each return value: Object.keys(obj) → ['name', 'age', 'email'] → string[] Object.values(obj) → ['Alice', 30, 'alice@example.com'] → any[] Object.entries(obj) → [['name', 'Alice'], ['age', 30], ...] → [string, any][] All three return ARRAYS. So you can chain array methods: const activeNames = Object.entries(users) .filter(([, user]) => user.active) .map(([id, user]) => user.name); This is the reason these methods exist. Objects don't have filter or map. Convert to an array of entries, then use array methods, then optionally convert back with Object.fromEntries(entries). Round-trip: const entries = Object.entries(obj); const back = Object.fromEntries(entries); // same as obj Object.fromEntries(entries) is the inverse. Very useful when you've transformed the entries into a new shape.
✅ When to use which
Three common scenarios.
const users = {
u1: { name: 'Alice', active: true },
u2: { name: 'Bob', active: false },
u3: { name: 'Charlie', active: true }
};
// 1. Check existence — keys
const hasUser = Object.keys(users).includes('u1');
console.log(hasUser); // true
// Or better: use 'in' operator
console.log('u1' in users); // true
console.log('u99' in users); // false
// 2. Aggregate values — values
const allNames = Object.values(users).map(u => u.name);
console.log(allNames);
// ['Alice', 'Bob', 'Charlie']
// 3. Filter and transform — entries
const activeUsers = Object.entries(users)
.filter(([, user]) => user.active)
.map(([id, user]) => ({ id, name: user.name }));
console.log(activeUsers);
// [{ id: 'u1', name: 'Alice' }, { id: 'u3', name: 'Charlie' }]
// 4. Transform keys or values — entries + fromEntries
const uppercasedNames = Object.fromEntries(
Object.entries(users).map(([id, user]) => [
id,
{ ...user, name: user.name.toUpperCase() }
])
);
console.log(uppercasedNames);
// { u1: {name: 'ALICE', ...}, u2: {...}, u3: {...} }
Which one to use:
✅ Existence check:
'key' in obj (fastest, handles all own + inherited)
obj.hasOwnProperty('key') (own only)
Object.keys(obj).includes('key') (works, slower)
Prefer `in` for existence.
Only use Object.keys().includes() if you specifically
want to check own enumerable properties.
✅ Get all values:
Object.values(obj)
Use when you don't need the keys.
For example: summing all scores, mapping names.
✅ Iterate with keys and values:
Object.entries(obj)
Use when you need both.
The destructuring pattern [key, value] is idiomatic.
✅ Transform:
Object.fromEntries(Object.entries(obj).map(...))
Use when you need to change the shape of an object.
Very common in data pipelines.
✅ Get all keys:
Object.keys(obj)
Use when you need to do something with the keys
themselves — sort them, filter them, compare sets.
The three methods return arrays.
So you chain array methods on them.
This is how you "iterate" an object in JS —
you don't. You convert to an array, then iterate
the array.
⚠️ Order of keys:
Integer-like keys come first, in numeric order.
Then string keys, in insertion order.
Symbols are excluded entirely.
For plain objects with string keys, insertion
order is preserved.
⚠️ These methods don't include inherited properties.
If you need them, use for...in with hasOwnProperty filter.
But you almost never need them.
✅ for...in vs Object.keys
The difference that matters.
// Create an object with an inherited property
const parent = { inherited: 'yes' };
const obj = Object.create(parent);
obj.own = 'yes';
// for...in includes inherited properties
console.log('for...in:');
for (const key in obj) {
console.log(' ', key);
}
// inherited
// own
// Object.keys — only own properties
console.log('Object.keys:');
Object.keys(obj).forEach(key => {
console.log(' ', key);
});
// own
// Object.entries — only own properties
console.log('Object.entries:');
Object.entries(obj).forEach(([key, value]) => {
console.log(' ', key, value);
});
// own yes
// If you must use for...in, filter with hasOwnProperty
console.log('for...in with filter:');
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
console.log(' ', key);
}
}
// own
The difference:
for...in:
→ iterates own AND inherited enumerable properties
→ includes properties from the prototype chain
→ works on any object
→ can't be chained with array methods
Object.keys / values / entries:
→ iterate OWN enumerable properties only
→ excludes inherited properties
→ returns arrays
→ chainable with array methods
For plain objects created with {} or Object.assign,
they give the same result — no inherited properties.
The difference only matters when the object has a
custom prototype, or was created with Object.create.
The modern recommendation:
Use Object.entries (or keys / values) for iteration.
Use for...in only when you specifically want
inherited properties (rare).
If you must use for...in, always filter with
hasOwnProperty — otherwise you'll include
properties you didn't expect.
The hasOwnProperty fix:
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
// process obj[key]
}
}
Why Object.prototype.hasOwnProperty.call(obj, key)?
Some objects override hasOwnProperty.
Using the prototype's version directly is safe.
Or use Object.hasOwn(obj, key) — ES2022, cleaner.
Object.hasOwn(obj, key) is the modern replacement.
Use it in new code.
⚠️ for...in also iterates array indices as strings.
for (const i in [1, 2, 3]) gives '0', '1', '2' (strings).
Never use for...in on arrays — use for...of or forEach.
Don't use for...in to iterate objects without hasOwnProperty. This is the classic prototype pollution bug — a library adds a property to Object.prototype, and suddenly every for...in loop iterates it. The result: unexpected properties in your data, crashes, or worse. The fix is simple: either use Object.keys/values/entries (which are immune by design), or add the hasOwnProperty filter. In modern code, prefer the Object methods — they're clearer and safer. for...in is a legacy pattern; Object.entries is the modern equivalent, and it's what you'd write anyway if you wanted to use array methods.
⚠️ Common mistakes
The object-method mistakes that cost real time.
Object.keys to include inherited propertiesIt doesn't — own only. Fix: use for...in with filter, or accept own-only.
for...in without hasOwnPropertyIncludes inherited properties. Fix: use Object.entries or add the filter.
for...in on arraysIterates string indices. Fix: use for...of or forEach.
Integer-like keys come first in numeric order. Fix: sort explicitly if order matters.
Object.fromEntries when transformingYou get an array, not an object. Fix: wrap with Object.fromEntries.
Object.entries on null or undefinedThrows. Fix: guard with obj && Object.entries(obj) or Object.entries(obj ?? {}).
Excluded from all three methods. Fix: use Object.getOwnPropertySymbols for symbols.
Excluded from all three methods. Fix: use Object.getOwnPropertyNames for non-enumerable.
❓ Frequently asked
What is the difference between Object.keys, Object.values, and Object.entries?
Three methods, three outputs. Object.keys(obj) returns an array of the property names. Object.values(obj) returns an array of the property values. Object.entries(obj) returns an array of [key, value] pairs. All three iterate own enumerable properties only. Use keys when you need the names, values when you need the values, entries when you need both.
What is the difference between Object.keys and for...in?
Object.keys returns own enumerable properties only. for...in iterates own AND inherited enumerable properties — including properties from the prototype chain. For plain objects, they give the same result. For objects created with a prototype, for...in can include inherited properties. Use Object.keys for reliable own-property iteration.
How do I iterate over an object's properties?
Three main ways. (1) Object.entries(obj).forEach(([key, value]) => ...) — modern, readable. (2) for (const [key, value] of Object.entries(obj)) — same, as a for...of loop. (3) Object.keys(obj).forEach(key => { const value = obj[key]; ... }). For simple iteration, entries is the cleanest.
Do these methods include inherited properties?
No. Object.keys, Object.values, and Object.entries iterate only own enumerable properties. Inherited properties are excluded. This is different from for...in, which includes inherited enumerable properties.
What order are the keys returned in?
Integer-like keys in ascending numeric order — '1' before '2' before '10'. String keys in insertion order. Symbol keys are excluded entirely. For plain string-keyed objects, insertion order is preserved. If order matters and you have numeric keys, sort explicitly rather than relying on the built-in order.
Are these methods fast?
Yes — they're implemented natively and iterate in O(n). For typical objects, the cost is negligible. The main performance consideration is allocation — each call creates a new array. If you're iterating the same object repeatedly, cache the result. But don't micro-optimize unless you've measured a problem.
🔗 Continue learning
Next in this series: