Working with Objects
The global Object provides tools for looping over objects, copying and merging them, converting them to and from arrays, and locking them against changes. Getters and setters let properties run code when read or written.
Keys, values and entries
const scores = { ana: 91, ben: 78, cleo: 85 };
console.log(Object.keys(scores));
console.log(Object.values(scores));
console.log(Object.entries(scores));
[ 'ana', 'ben', 'cleo' ] [ 91, 78, 85 ] [ [ 'ana', 91 ], [ 'ben', 78 ], [ 'cleo', 85 ] ]
These turn an object into an array, which unlocks every array method:
const scores = { ana: 91, ben: 78, cleo: 85 }; const average = Object.values(scores).reduce((a, b) => a + b, 0) / 3; console.log(average.toFixed(1)); const curved = Object.fromEntries( Object.entries(scores).map(([name, score]) => [name, Math.min(100, score + 5)]) ); console.log(curved); const honours = Object.entries(scores).filter(([, s]) => s >= 85).map(([n]) => n); console.log(honours);
84.7
{ ana: 96, ben: 83, cleo: 90 }
[ 'ana', 'cleo' ]Object.fromEntries reverses Object.entries, turning [key, value] pairs back into an object. The entries → array methods → fromEntries pattern is the standard way to "map over an object".
Copying and merging
const defaults = { theme: "light", lang: "en" }; const saved = { theme: "dark" }; const viaSpread = { ...defaults, ...saved }; const viaAssign = Object.assign({}, defaults, saved); console.log(viaSpread, viaAssign); Object.assign(defaults, { lang: "fr" }); console.log(defaults);
{ theme: 'dark', lang: 'en' } { theme: 'dark', lang: 'en' }
{ theme: 'light', lang: 'fr' }Object.assign(target, ...sources) copies properties into target and modifies it. Spread is usually clearer. Both are shallow copies; use structuredClone for deep copies (lesson 33).
Freezing and sealing
"use strict"; const config = Object.freeze({ apiUrl: "https://api.example.com", retries: 3 }); try { config.retries = 10; } catch (err) { console.log(err.constructor.name); } console.log(config.retries, Object.isFrozen(config));
TypeError 3 true
Object.freeze prevents adding, removing or changing properties. In strict mode a change throws; in sloppy mode it fails silently. Object.seal is a lighter version that allows changing existing values but not adding or removing properties. Both are shallow — nested objects stay changeable unless frozen too.
Getters and setters
A getter runs a function when a property is read; a setter runs when it's assigned. From outside, they look like ordinary properties:
const temperature = { celsius: 20, get fahrenheit() { return this.celsius * 9 / 5 + 32; }, set fahrenheit(value) { this.celsius = (value - 32) * 5 / 9; }, }; console.log(temperature.fahrenheit); temperature.fahrenheit = 212; console.log(temperature.celsius);
68 100
Getters are ideal for values computed from other properties, so they're never out of date. Setters are a natural place to validate.
Optional chaining and nullish defaults
const responses = [ { user: { profile: { avatar: "a.png" } } }, { user: { profile: null } }, {}, ]; for (const r of responses) { console.log(r.user?.profile?.avatar ?? "default.png"); }
a.png default.png default.png
Property order
Object keys keep a predictable order: integer-like keys first in ascending numeric order, then string keys in the order they were added. This affects Object.keys, for…in and JSON.stringify:
const mixed = { b: 1, 10: "ten", a: 2, 2: "two" }; console.log(Object.keys(mixed));
[ '2', '10', 'b', 'a' ]
Useful Object methods
| Method | Purpose |
|---|---|
Object.keys / values / entries | Object → array |
Object.fromEntries | Array of pairs (or a Map) → object |
Object.assign | Copy properties into a target |
Object.hasOwn(obj, key) | Does the object itself have this key? |
Object.freeze / seal | Lock an object against changes |
Object.groupBy(items, fn) | Group an array into an object (lesson 31) |
structuredClone(value) | Deep copy |