Skip to content
elephantoo

Objects in depth

Lesson 11 of 34 16 min read

Shorthand, computed keys, methods, Object.keys/entries, copying, freezing and structuredClone.


You met objects in Arrays & objects. Objects are central to JavaScript — arrays, functions, dates and DOM elements are all objects — so it pays to know them well. This lesson covers modern object syntax, the Object helper functions, copying, comparing and freezing.

Modern object literal syntax#

Property shorthand

JavaScript
const name = "Ada";
const role = "admin";

const user = { name, role }; // same as { name: name, role: role }
console.log(user); // { name: 'Ada', role: 'admin' }

Method shorthand

JavaScript
const timer = {
  seconds: 90,
  format() {
    const m = Math.floor(this.seconds / 60);
    const s = String(this.seconds % 60).padStart(2, "0");
    return `${m}:${s}`;
  },
};
console.log(timer.format()); // 1:30

Computed property names

Put an expression in [ ] to compute a key:

JavaScript
const field = "email";
const form = {
  [field]: "ada@example.com",
  [`${field}Verified`]: true,
};
console.log(form); // { email: 'ada@example.com', emailVerified: true }

This is common when updating form state: { ...values, [event.target.name]: event.target.value }.

Getters and setters#

A getter looks like a property but runs a function; a setter runs when you assign:

JavaScript
const temperature = {
  celsius: 25,
  get fahrenheit() {
    return this.celsius * 9 / 5 + 32;
  },
  set fahrenheit(f) {
    this.celsius = (f - 32) * 5 / 9;
  },
};

console.log(temperature.fahrenheit); // 77
temperature.fahrenheit = 212;
console.log(temperature.celsius);    // 100

Checking and listing properties#

JavaScript
const book = { title: "Eloquent JS", pages: 450, author: undefined };

console.log("author" in book);           // true – exists (value is undefined)
console.log(Object.hasOwn(book, "isbn")); // false
console.log(book.isbn === undefined);    // true – but can't tell "missing" from "undefined"

console.log(Object.keys(book));    // [ 'title', 'pages', 'author' ]
console.log(Object.values(book));  // [ 'Eloquent JS', 450, undefined ]
console.log(Object.entries(book)); // [ [ 'title', 'Eloquent JS' ], [ 'pages', 450 ], [ 'author', undefined ] ]

Object.hasOwn(obj, key) is the modern replacement for obj.hasOwnProperty(key).

Transforming objects with entries

Object.entries turns an object into an array (so you can use array methods), and Object.fromEntries turns it back:

JavaScript
const prices = { tea: 20, coffee: 45, cake: 120 };

const discounted = Object.fromEntries(
  Object.entries(prices).map(([item, price]) => [item, price * 0.9])
);
console.log(discounted); // { tea: 18, coffee: 40.5, cake: 108 }

const affordable = Object.fromEntries(
  Object.entries(prices).filter(([, price]) => price < 100)
);
console.log(affordable); // { tea: 20, coffee: 45 }

Property order#

Object keys keep insertion order, with one exception: integer-like keys come first, in ascending order.

JavaScript
const mixed = { b: 1, 2: "two", a: 2, 1: "one" };
console.log(Object.keys(mixed)); // [ '1', '2', 'b', 'a' ]

All keys are strings (or symbols) — obj[1] and obj["1"] are the same property. Need non-string keys or guaranteed order? Use a Map.

Copying objects#

Shallow copies

JavaScript
const original = { name: "Ada", address: { city: "London" } };

const copy1 = { ...original };            // spread
const copy2 = Object.assign({}, original); // older equivalent

copy1.name = "Grace";
console.log(original.name); // Ada – top level was copied

copy1.address.city = "Paris";
console.log(original.address.city); // Paris 😱 – nested object is shared!

A shallow copy duplicates only the top level. Nested objects are still shared references.

Deep copies with structuredClone

JavaScript
const state = { user: { name: "Ada", tags: ["admin"] }, createdAt: new Date(0) };
const deep = structuredClone(state);

deep.user.tags.push("editor");
console.log(state.user.tags);                   // [ 'admin' ] – untouched
console.log(deep.createdAt instanceof Date);    // true – Dates survive

structuredClone (built into browsers and Node 17+) handles nested objects, arrays, Dates, Maps and Sets. It can't clone functions or DOM nodes. The old trick JSON.parse(JSON.stringify(obj)) loses Dates, undefined and more — avoid it.

Updating immutably

Rather than mutating, create an updated copy — a pattern you'll use constantly in React:

JavaScript
const profile = { name: "Ada", settings: { theme: "light", lang: "en" } };

const updated = {
  ...profile,
  settings: { ...profile.settings, theme: "dark" },
};

console.log(profile.settings.theme); // light
console.log(updated.settings);       // { theme: 'dark', lang: 'en' }

Comparing objects#

=== compares identity (is it the same object?), not contents:

JavaScript
const a = { x: 1 };
const b = { x: 1 };
const c = a;
console.log(a === b); // false
console.log(a === c); // true

// A simple content comparison for flat objects:
function shallowEqual(o1, o2) {
  const k1 = Object.keys(o1);
  const k2 = Object.keys(o2);
  return k1.length === k2.length && k1.every((k) => o1[k] === o2[k]);
}
console.log(shallowEqual(a, b)); // true

For deep comparison in tests, use your test library's toEqual; in Node there's util.isDeepStrictEqual.

Freezing and sealing#

JavaScript
const config = Object.freeze({ apiUrl: "https://api.example.com", retries: 3, flags: { beta: false } });

config.retries = 10;      // ignored (TypeError in strict mode)
config.flags.beta = true; // allowed – freeze is shallow!
console.log(config.retries, config.flags.beta); // 3 true
console.log(Object.isFrozen(config));           // true

Object.seal is a weaker version: existing properties can change, but none can be added or removed. Both are shallow.

Optional chaining and nullish values#

Real data often has missing pieces. ?. stops and returns undefined instead of throwing:

JavaScript
const order = { id: 1, customer: { name: "Ada" } };
console.log(order.customer?.address?.city);           // undefined
console.log(order.customer?.address?.city ?? "N/A");  // N/A
// console.log(order.customer.address.city);         // TypeError!

More in Modern JavaScript features.

Objects as dictionaries#

Objects are often used as lookup tables:

JavaScript
const wordCounts = {};
for (const word of "the cat and the hat".split(" ")) {
  wordCounts[word] = (wordCounts[word] ?? 0) + 1;
}
console.log(wordCounts); // { the: 2, cat: 1, and: 1, hat: 1 }

Beware: keys like "constructor" or "__proto__" can clash with built-in properties. For user-controlled keys, use Object.create(null) or a Map.

JSON: objects as text#

JSON is the text format for sending objects over the network or saving them:

JavaScript
const data = { name: "Ada", skills: ["math", "code"], active: true };
const text = JSON.stringify(data);
console.log(text);                      // {"name":"Ada","skills":["math","code"],"active":true}
console.log(JSON.parse(text).skills[1]); // code
console.log(JSON.stringify(data, null, 2).split("\n").length); // 8 lines – pretty-printed

JSON keys must be double-quoted strings; functions and undefined values are dropped. You'll use JSON heavily in fetch, APIs & JSON.

Common mistakes#

  • Assuming { ...obj } is a deep copy.
  • Comparing objects with === and expecting a content check.
  • Using obj.hasOwnProperty on objects created with Object.create(null) — use Object.hasOwn.
  • Thinking Object.freeze is recursive.

What's next#

Next: destructuring and spread — concise syntax for unpacking, copying and merging arrays and objects.

Check your understanding

Quick quiz

0/3 answered
  1. 1.Given const name = "Ada"; const user = { name };, what is user?

  2. 2.After const b = { ...a };, which statement is true?

  3. 3.What does Object.freeze(settings) do?

Finished reading?

Mark this lesson complete to track your progress.