Objects - Structured Data
TL;DR — Quick Summary
- Use dot notation for known keys, bracket notation for dynamic or computed keys.
- Destructuring extracts properties into variables — use it in function parameters to document what you need from an object.
- Spread (
{ ...obj }) does a shallow copy — nested objects are still shared by reference. Object.entries()+for...ofis the clean modern way to iterate an object's key-value pairs.
Lesson Overview
Objects are the foundation of JavaScript — almost everything in JS is an object or behaves like one. An object groups related data and behaviour under one name: a user object has a name, an email, a login() method. This mirrors how we think about real-world entities.
Objects use key-value pairs: keys are strings (or Symbols), values can be anything — primitives, arrays, other objects, or functions. When a function is a value in an object, it's called a method.
Modern JavaScript adds powerful syntax: shorthand properties, computed keys, destructuring, spread/rest, and optional chaining — all essential for working with objects in real codebases.
Conceptual Deep Dive
Creating objects:
- Object literal:
const user = { name: 'Alice', age: 30 } - Constructor function:
function User(name) { this.name = name; } - Class:
class User { constructor(name) { this.name = name; } } Object.create(proto): creates with specific prototype
Accessing and modifying:
- Dot notation:
user.name - Bracket notation:
user['name']oruser[dynamicKey] - Optional chaining:
user?.address?.city
Object methods:
Object.keys(obj): array of keysObject.values(obj): array of valuesObject.entries(obj): array of [key, value] pairsObject.assign(target, source): shallow mergeObject.freeze(obj): make immutable{ ...obj }: spread (shallow copy/merge)
Implementation Lab
// Object literal
const product = {
id: 42,
name: 'Mechanical Keyboard'Keyboard',
price: 149.99,
inStock: true,
tags: ['tech', 'productivity'],
// Method
getDisplayPrice() {
return `$${this.price.toFixed(2)}`this.price.toFixed(2)}`;
},
// Arrow function doesn't work for 'this'function doesn't work for 'this'
};
// Access
console.log(product.name); // dot notation
console.log(product['price']); // bracket notation (same result))
const key = 'inStock';
console.log(product[key]); // dynamic key access — must use brackets
// Modify
product.price = 129.99; // update existing
product.discount = 0.1; // add new property
delete product.discount; // remove property
// Methods
console.log(product.getDisplayPrice()); // '$149.99'
// Shorthand property syntax
const name = 'Alice';
const age = 30;
const user = { name, age }; // same as { name: name, age: age }}
// Computed property keys
const field = 'score';
const gameData = { [field]: 100, [`${field}History`}History`]: [] };
console.log(gameData.score); // 100const user = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
address: { city: 'London', country: 'UK' },
role: 'admin'
};
// Destructuring: unpack properties into variables
const { name, email, role } = user;
console.log(name, email); // 'Alice' 'alice@example.com''alice@example.com'
// Rename during destructuring
const { name: userName, role: userRole = 'viewer' } = user;
console.log(userName); // 'Alice'
// Nested destructuring
const { address: { city, country } } = user;
console.log(city); // 'London'
// Rest in destructuring
const { id, ...publicData } = user;
console.log(publicData); // { name, email, address, role } — no id} — no id
// Spread: shallow copy and merge
const updated = { ...user, role: 'editor', lastSeen: new Date() };
// user is unchanged, updated has all user's props + overrides
// Merge two objects
const defaults = { theme: 'dark', language: 'en', fontSize: 14 };
const userPrefs = { theme: 'light', fontSize: 16 };
const settings = { ...defaults, ...userPrefs }; // userPrefs overrides defaults
console.log(settings); // { theme: 'light', language: 'en', fontSize: 16 }'light', language: 'en', fontSize: 16 }
// Object.entries + destructuring = elegant iteration
const scores = { alice: 95, bob: 78, charlie: 88 };
for (const [player, score] of Object.entries(scores)) {
console.log(`${player}: ${score}`}: ${score}`);
}
// Object.fromEntries: array of pairs → object (inverse of entries)(inverse of entries)
const doubled = Object.fromEntries(
Object.entries(scores).map(([k, v]) => [k, v * 2])
);
console.log(doubled); // { alice: 190, bob: 156, charlie: 176 }190, bob: 156, charlie: 176 }Pro Tips — Senior Dev Insights
Use structuredClone(obj) (modern) or JSON.parse(JSON.stringify(obj)) (legacy) for deep copies when you need complete independence from the original.
Object.freeze() makes an object shallow-immutable — useful for constants like config and enums: const ROLES = Object.freeze({ ADMIN: 'admin', EDITOR: 'editor' }).
Destructure in function parameters to make dependencies explicit and enable default values: function createUser({ name, role = 'viewer', active = true } = {}).
Common Developer Pitfalls
{ ...obj } only copies the top level. Nested objects are copied by reference: changing a nested property affects both copies. Use structuredClone(obj) for deep copies.this won't refer to the object. Use shorthand method syntax: { greet() { return this.name; } }.{ ...obj }) before modifying.if (obj.key) — this fails when the value is falsy (0, '', false). Use 'key' in obj or Object.hasOwn(obj, 'key').Interview Mastery
Dot notation (obj.key) is cleaner and more common — use it when the key is a valid identifier and known at write time. Bracket notation (obj['key'] or obj[variable]) is required when: the key is stored in a variable, the key contains special characters or spaces, or the key is dynamic/computed. Example: const field = 'name'; obj[field] works; obj.field would look for a property literally named 'field'.
The spread operator { ...obj } creates a shallow copy of an object — all top-level key-value pairs are copied into a new object. You can override properties: { ...defaults, theme: 'dark' } — later properties override earlier ones. Limitation: it's shallow. Nested objects are not copied — they're shared by reference. Changing a nested property on the copy also changes the original. For deep copies, use structuredClone() (modern) or JSON.parse(JSON.stringify()) (limited to JSON-safe values).
Several ways: 'key' in obj returns true if the property exists (including inherited). Object.hasOwn(obj, 'key') checks only own properties (modern, preferred). obj.hasOwnProperty('key') — older equivalent. Avoid if (obj.key) — this returns false for falsy values (0, '', false, null) even if the property exists. For optional chaining with fallback: obj?.key ?? defaultValue.
Hands-on Lab Exercises
this correctly in methods.Write a function that merges two settings objects, with the second taking precedence. Handle nested objects correctly.
Object.entries and Object.fromEntries to write a function that doubles all numeric values in an object.Practice destructuring: extract nested properties from a deeply nested API response object in a single destructuring assignment.
Real-World Practice Scenarios
Deepen Your Knowledge
Objects - Structured Data
TL;DR — Quick Summary
- Use dot notation for known keys, bracket notation for dynamic or computed keys.
- Destructuring extracts properties into variables — use it in function parameters to document what you need from an object.
- Spread (
{ ...obj }) does a shallow copy — nested objects are still shared by reference. Object.entries()+for...ofis the clean modern way to iterate an object's key-value pairs.
Overview
Objects are the foundation of JavaScript — almost everything in JS is an object or behaves like one. An object groups related data and behaviour under one name: a user object has a name, an email, a login() method. This mirrors how we think about real-world entities.
Objects use key-value pairs: keys are strings (or Symbols), values can be anything — primitives, arrays, other objects, or functions. When a function is a value in an object, it's called a method.
Modern JavaScript adds powerful syntax: shorthand properties, computed keys, destructuring, spread/rest, and optional chaining — all essential for working with objects in real codebases.
Deep Dive Analysis
<p><strong>Creating objects:</strong></p><ul><li>Object literal: <code>const user = { name: 'Alice', age: 30 }</code></li><li>Constructor function: <code>function User(name) { this.name = name; }</code></li><li>Class: <code>class User { constructor(name) { this.name = name; } }</code></li><li><code>Object.create(proto)</code>: creates with specific prototype</li></ul><p><strong>Accessing and modifying:</strong></p><ul><li>Dot notation: <code>user.name</code></li><li>Bracket notation: <code>user['name']</code> or <code>user[dynamicKey]</code></li><li>Optional chaining: <code>user?.address?.city</code></li></ul><p><strong>Object methods:</strong></p><ul><li><code>Object.keys(obj)</code>: array of keys</li><li><code>Object.values(obj)</code>: array of values</li><li><code>Object.entries(obj)</code>: array of [key, value] pairs</li><li><code>Object.assign(target, source)</code>: shallow merge</li><li><code>Object.freeze(obj)</code>: make immutable</li><li><code>{ ...obj }</code>: spread (shallow copy/merge)</li></ul>
Implementation Reference
// Object literal
const product = {
id: 42,
name: 'Mechanical Keyboard',
price: 149.99,
inStock: true,
tags: ['tech', 'productivity'],
// Method
getDisplayPrice() {
return `$${this.price.toFixed(2)}`;
},
// Arrow function doesn't work for 'this'
};
// Access
console.log(product.name); // dot notation
console.log(product['price']); // bracket notation (same result)
const key = 'inStock';
console.log(product[key]); // dynamic key access — must use brackets
// Modify
product.price = 129.99; // update existing
product.discount = 0.1; // add new property
delete product.discount; // remove property
// Methods
console.log(product.getDisplayPrice()); // '$149.99'
// Shorthand property syntax
const name = 'Alice';
const age = 30;
const user = { name, age }; // same as { name: name, age: age }
// Computed property keys
const field = 'score';
const gameData = { [field]: 100, [`${field}History`]: [] };
console.log(gameData.score); // 100const user = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
address: { city: 'London', country: 'UK' },
role: 'admin'
};
// Destructuring: unpack properties into variables
const { name, email, role } = user;
console.log(name, email); // 'Alice' 'alice@example.com'
// Rename during destructuring
const { name: userName, role: userRole = 'viewer' } = user;
console.log(userName); // 'Alice'
// Nested destructuring
const { address: { city, country } } = user;
console.log(city); // 'London'
// Rest in destructuring
const { id, ...publicData } = user;
console.log(publicData); // { name, email, address, role } — no id
// Spread: shallow copy and merge
const updated = { ...user, role: 'editor', lastSeen: new Date() };
// user is unchanged, updated has all user's props + overrides
// Merge two objects
const defaults = { theme: 'dark', language: 'en', fontSize: 14 };
const userPrefs = { theme: 'light', fontSize: 16 };
const settings = { ...defaults, ...userPrefs }; // userPrefs overrides defaults
console.log(settings); // { theme: 'light', language: 'en', fontSize: 16 }
// Object.entries + destructuring = elegant iteration
const scores = { alice: 95, bob: 78, charlie: 88 };
for (const [player, score] of Object.entries(scores)) {
console.log(`${player}: ${score}`);
}
// Object.fromEntries: array of pairs → object (inverse of entries)
const doubled = Object.fromEntries(
Object.entries(scores).map(([k, v]) => [k, v * 2])
);
console.log(doubled); // { alice: 190, bob: 156, charlie: 176 }Common Pitfalls
- •Assuming spread creates a deep copy — <code>{ ...obj }</code> only copies the top level. Nested objects are copied by reference: changing a nested property affects both copies. Use <code>structuredClone(obj)</code> for deep copies.
- •Using arrow functions as object methods — <code>this</code> won't refer to the object. Use shorthand method syntax: <code>{ greet() { return this.name; } }</code>.
- •Accidentally mutating an object passed to a function — objects are passed by reference in JavaScript. Always create a copy (<code>{ ...obj }</code>) before modifying.
- •Checking if an object has a property with <code>if (obj.key)</code> — this fails when the value is falsy (0, '', false). Use <code>'key' in obj</code> or <code>Object.hasOwn(obj, 'key')</code>.
Hands-on Practice
- ✓Model a bank account as an object with properties (balance, owner, accountType) and methods (deposit, withdraw, getStatement). Use <code>this</code> correctly in methods.
- ✓Write a function that merges two settings objects, with the second taking precedence. Handle nested objects correctly.
- ✓Use <code>Object.entries</code> and <code>Object.fromEntries</code> to write a function that doubles all numeric values in an object.
- ✓Practice destructuring: extract nested properties from a deeply nested API response object in a single destructuring assignment.
Expert Pro Tips
Interview Preparation
Q: What's the difference between dot notation and bracket notation?
Master Answer:
Dot notation (obj.key) is cleaner and more common — use it when the key is a valid identifier and known at write time. Bracket notation (obj['key'] or obj[variable]) is required when: the key is stored in a variable, the key contains special characters or spaces, or the key is dynamic/computed. Example: const field = 'name'; obj[field] works; obj.field would look for a property literally named 'field'.
Q: What does object spread do and what are its limitations?
Master Answer:
The spread operator { ...obj } creates a shallow copy of an object — all top-level key-value pairs are copied into a new object. You can override properties: { ...defaults, theme: 'dark' } — later properties override earlier ones. Limitation: it's shallow. Nested objects are not copied — they're shared by reference. Changing a nested property on the copy also changes the original. For deep copies, use structuredClone() (modern) or JSON.parse(JSON.stringify()) (limited to JSON-safe values).
Q: How do you check if an object has a specific property?
Master Answer:
Several ways: 'key' in obj returns true if the property exists (including inherited). Object.hasOwn(obj, 'key') checks only own properties (modern, preferred). obj.hasOwnProperty('key') — older equivalent. Avoid if (obj.key) — this returns false for falsy values (0, '', false, null) even if the property exists. For optional chaining with fallback: obj?.key ?? defaultValue.
Simulated Scenarios
Extended Reading
MDN: Working with objects
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
JavaScript.info: Objects
https://javascript.info/object
JavaScript.info: Destructuring
https://javascript.info/destructuring-assignment
© 2026 DevHub Engineering • All Proprietary Rights Reserved
Generated on March 7, 2026 • Ver: 4.0.2
Document Class: Master Education
Confidential Information • Licensed to User