Object.keys, Object.values, Object.entries
You have an object. You need to loop through it. You reach for for...in and suddenly you're dealing with inherited properties, prototype chain issues, and hasOwnProperty checks everywhere. There had to be a better way.
In ES2017, JavaScript gave us three elegant methods that turn objects into arrays you can actually work with: Object.keys(), Object.values(), and Object.entries(). Let's see why they've become essential tools in every developer's toolkit.
What Are These Methods?
These three static methods on the Object constructor each give you a different "view" of an object as an array:
Object.keys(obj)— Returns an array of the object's own enumerable property names (the keys)Object.values(obj)— Returns an array of the object's own enumerable property valuesObject.entries(obj)— Returns an array of[key, value]pairs
The key word here is own. These methods only return properties that belong directly to the object, not inherited ones from the prototype chain. And they only return enumerable properties — the ones that would show up in a for...in loop.
Why Do These Exist?
Before these methods, iterating over an object was awkward:
// The old way — verbose and error-prone
const user = { name: "Dima", role: "developer" };
for (const key in user) {
if (user.hasOwnProperty(key)) { // Don't forget this check!
console.log(key, user[key]);
}
}
You had to remember the hasOwnProperty check every time, or risk iterating over inherited properties. Plus, for...in gives you keys — if you wanted values or both together, you had to do extra work.
Object.keys() arrived in ES5, and Object.values() and Object.entries() followed in ES2017. Together, they let you convert objects to arrays, which opens up the entire arsenal of array methods: map, filter, reduce, find, and more.
Let's See Them in Action
Basic Example: Extracting Keys, Values, and Entries
const product = {
name: "Laptop",
price: 999,
inStock: true
};
console.log(Object.keys(product));
// Output: ["name", "price", "inStock"]
console.log(Object.values(product));
// Output: ["Laptop", 999, true]
console.log(Object.entries(product));
// Output: [["name", "Laptop"], ["price", 999], ["inStock", true]]
Notice how Object.entries() returns nested arrays — each inner array is a [key, value] pair. This format works beautifully with destructuring.
Real-World Usage: Transforming and Filtering Objects
Here's where these methods really shine — combining them with array methods:
const prices = {
apple: 1.5,
banana: 0.75,
orange: 2.0,
grape: 3.5
};
// Find all items under $2
const affordable = Object.entries(prices)
.filter(([fruit, price]) => price < 2)
.map(([fruit]) => fruit);
console.log(affordable);
// Output: ["apple", "banana"]
// Calculate total inventory value
const quantities = { apple: 10, banana: 25, orange: 15, grape: 8 };
const totalValue = Object.entries(prices).reduce((sum, [fruit, price]) => {
return sum + (price * quantities[fruit]);
}, 0);
console.log(totalValue);
// Output: 91.75
The destructuring in the callback — ([fruit, price]) — turns each [key, value] pair into named variables. This pattern is everywhere in modern JavaScript.
Converting Back to an Object
Object.entries() has a perfect counterpart: Object.fromEntries(). Together, they let you transform objects:
const original = { a: 1, b: 2, c: 3 };
// Double all values
const doubled = Object.fromEntries(
Object.entries(original).map(([key, value]) => [key, value * 2])
);
console.log(doubled);
// Output: { a: 2, b: 4, c: 6 }
// Filter out specific keys
const settings = { theme: "dark", fontSize: 16, debugMode: true };
const publicSettings = Object.fromEntries(
Object.entries(settings).filter(([key]) => key !== "debugMode")
);
console.log(publicSettings);
// Output: { theme: "dark", fontSize: 16 }
This entries → transform → fromEntries pattern is the object equivalent of array's map and filter.
Watch Out: Property Order
Object property order in JavaScript follows specific rules that can surprise you:
const mixed = {
b: 2,
1: "one",
a: 1,
2: "two"
};
console.log(Object.keys(mixed));
// Output: ["1", "2", "b", "a"]
Wait, what? Here's the rule: integer keys come first (in numeric order), then string keys (in insertion order).
This matters when you're relying on key order:
// Building a form from an object? Order matters!
const formFields = {
email: { type: "email", required: true },
password: { type: "password", required: true },
2: { type: "hidden" }, // This will appear FIRST
name: { type: "text", required: false }
};
Object.entries(formFields).forEach(([field, config]) => {
console.log(field); // "2", "email", "password", "name" — not what you expected!
});
If order matters, either avoid numeric keys or use a Map instead.
Watch Out: Non-Objects
What happens if you pass something that isn't an object?
// Strings get split into characters with numeric indices
console.log(Object.keys("hello"));
// Output: ["0", "1", "2", "3", "4"]
console.log(Object.values("hello"));
// Output: ["h", "e", "l", "l", "o"]
// Numbers and booleans return empty arrays
console.log(Object.keys(42)); // Output: []
console.log(Object.values(true)); // Output: []
// null and undefined throw errors!
Object.keys(null); // TypeError: Cannot convert undefined or null to object
Object.keys(undefined); // TypeError
Always validate your input if it might be null or undefined:
const safeKeys = (obj) => Object.keys(obj ?? {});
Advanced: Working with Symbols
Object.keys(), Object.values(), and Object.entries() silently ignore Symbol keys:
const id = Symbol("id");
const user = {
name: "Dima",
[id]: 12345
};
console.log(Object.keys(user));
// Output: ["name"] — the symbol is missing!
// To get symbol keys, use:
console.log(Object.getOwnPropertySymbols(user));
// Output: [Symbol(id)]
// Or get everything with Reflect.ownKeys:
console.log(Reflect.ownKeys(user));
// Output: ["name", Symbol(id)]
This is usually what you want — symbols are meant for "hidden" properties. But if you need them, reach for Reflect.ownKeys().
Interview Challenge: What Does This Output?
const proto = { inherited: "value" };
const obj = Object.create(proto);
obj.own = "property";
console.log(Object.keys(obj));
console.log(Object.values(obj));
for (const key in obj) {
console.log(key);
}
Click to see the answer
console.log(Object.keys(obj)); // ["own"]
console.log(Object.values(obj)); // ["property"]
for (const key in obj) {
console.log(key);
}
// "own"
// "inherited" — for...in includes inherited properties!
This demonstrates the key advantage of Object.keys/values/entries — they only return own properties, while for...in walks the entire prototype chain.
How to Think About This
Think of these three methods as different lenses for viewing an object:
| Method | Returns | Use When |
|--------|---------|----------|
| Object.keys() | Property names | You need to know what keys exist, or iterate without values |
| Object.values() | Property values | You only care about the data, not the keys |
| Object.entries() | [key, value] pairs | You need both, or want to transform the object |
Here's a decision framework:
Need to iterate over an object?
├── Need keys only? → Object.keys()
├── Need values only? → Object.values()
├── Need both key and value? → Object.entries()
└── Need to include inherited/symbols? → for...in or Reflect.ownKeys()
💡 Remember: These methods return arrays, so you get immediate access to
map,filter,reduce,find, and every other array method. That's their superpower.
Test Yourself
Before moving on, make sure you can answer:
- What's the difference between
Object.keys()andfor...inwhen iterating an object? - In what order does
Object.keys()return properties if some keys are numbers? - How would you create a new object with all values doubled using
Object.entries()? - What happens if you call
Object.keys(null)? - How can you get both string keys and Symbol keys from an object?
Related Topics
for...inloops — The older way to iterate objects; includes inherited properties unlike these methodsObject.fromEntries()— The inverse ofObject.entries(); converts[key, value]pairs back to an object- Maps — If you need guaranteed insertion order or non-string keys,
Mapis often a better choice - Destructuring — Essential for cleanly working with
Object.entries()results - Prototype Chain — Understanding why "own" properties matter requires knowing how inheritance works
Go Deeper
- MDN: Object.keys() — Complete reference with edge cases
- MDN: Object.entries() — Includes browser compatibility notes
- javascript.info: Object keys, values, entries — Interactive examples and exercises
- 2ality: Object.entries() and Object.values() — Axel Rauschmayer's deep dive into the ES2017 additions