Object Static Methods
Plain objects don’t come with iteration or transformation methods of their own — instead, the Object constructor hosts a family of static methods that operate on any object you pass in. These methods are the bridge between objects and arrays: they let you enumerate keys, merge sources, build objects from key/value pairs, and lock data down against mutation. Mastering them turns the humble object into a flexible, array-friendly data structure.
Reading object contents: keys, values, entries
The three workhorse methods are Object.keys, Object.values, and Object.entries. Each takes an object and returns an array, which means you can immediately reach for array methods like map, filter, and forEach. They only enumerate the object’s own, enumerable, string-keyed properties — inherited properties and Symbol keys are skipped.
const product = { id: 42, name: "Keyboard", price: 89 };
console.log(Object.keys(product));
console.log(Object.values(product));
console.log(Object.entries(product));
Output:
[ 'id', 'name', 'price' ]
[ 42, 'Keyboard', 89 ]
[ [ 'id', 42 ], [ 'name', 'Keyboard' ], [ 'price', 89 ] ]
Object.entries is especially powerful because it pairs cleanly with array destructuring inside a for...of loop, giving you both the key and value in one tidy statement.
const scores = { math: 92, science: 88, history: 75 };
for (const [subject, score] of Object.entries(scores)) {
console.log(`${subject}: ${score}`);
}
// Or transform with array methods
const passing = Object.entries(scores)
.filter(([, score]) => score >= 80)
.map(([subject]) => subject);
console.log(passing);
Output:
math: 92
science: 88
history: 75
[ 'math', 'science' ]
Property order is mostly predictable: integer-like keys come first in ascending numeric order, then string keys in insertion order. Don’t rely on order for non-integer keys across very old engines, but in any modern runtime it is stable.
Merging objects with Object.assign
Object.assign(target, ...sources) copies all own enumerable properties from each source into the target, mutating and returning the target. Later sources overwrite earlier ones, making it ideal for applying defaults or shallow-merging configuration.
const defaults = { theme: "light", fontSize: 14, showSidebar: true };
const userPrefs = { theme: "dark", fontSize: 16 };
const settings = Object.assign({}, defaults, userPrefs);
console.log(settings);
Output:
{ theme: 'dark', fontSize: 16, showSidebar: true }
Passing a fresh {} as the target avoids mutating defaults. Note the copy is shallow — nested objects are shared by reference, not cloned. For most merges the object spread operator ({ ...defaults, ...userPrefs }) is the cleaner modern equivalent.
Building objects with Object.fromEntries
Object.fromEntries is the inverse of Object.entries: it takes a list of [key, value] pairs and assembles an object. This closes the loop, letting you convert an object to an array, transform it, and convert it back.
const prices = { shirt: 20, pants: 40, hat: 15 };
// Apply a 10% discount to every value
const discounted = Object.fromEntries(
Object.entries(prices).map(([item, price]) => [item, price * 0.9])
);
console.log(discounted);
Output:
{ shirt: 18, pants: 36, hat: 13.5 }
Because it accepts any iterable of pairs, Object.fromEntries also converts a Map straight into a plain object, and pairs neatly with URLSearchParams to turn a query string into an object.
const params = new URLSearchParams("page=2&sort=price&limit=20");
const query = Object.fromEntries(params);
console.log(query);
Output:
{ page: '2', sort: 'price', limit: '20' }
Converting between objects and arrays
The entries/fromEntries pair makes object-to-array round-trips trivial. The table below summarizes which method to reach for.
| Goal | Method | Returns |
|---|---|---|
| Get all keys | Object.keys(obj) | string[] |
| Get all values | Object.values(obj) | any[] |
| Get key/value pairs | Object.entries(obj) | [string, any][] |
| Build object from pairs | Object.fromEntries(pairs) | object |
| Shallow-merge sources | Object.assign(target, ...src) | the target object |
| Map/Set/URLSearchParams to object | Object.fromEntries(iterable) | object |
Locking objects down (a teaser)
Two static methods restrict how an object can change. Object.freeze(obj) makes it fully immutable — no adding, removing, or changing properties. Object.seal(obj) is gentler: you can still edit existing values but cannot add or delete keys.
const config = Object.freeze({ apiUrl: "https://api.example.com" });
config.apiUrl = "https://evil.example.com"; // silently ignored
console.log(config.apiUrl);
console.log(Object.isFrozen(config));
Output:
https://api.example.com
true
Freezing is shallow. A frozen object’s nested objects remain mutable, so deep immutability requires recursively freezing every level. See the immutability page for a full treatment.
Best practices
- Prefer
Object.entrieswithfor...ofdestructuring overfor...in— it skips inherited keys and avoids prototype surprises. - Always pass a fresh
{}as the first argument toObject.assignto avoid mutating an existing source object. - Reach for the spread syntax (
{ ...a, ...b }) for simple merges; saveObject.assignfor dynamic source lists. - Combine
Object.entriesandObject.fromEntriesto transform objects functionally instead of mutating in place. - Remember all these methods are shallow — clone or freeze nested structures explicitly when you need deep behavior.
- Use
Object.freezefor true constants like configuration objects to catch accidental writes (in strict mode they throw).