04 - Objects

What are Objects?

Objects store collections of key-value pairs. They're perfect for representing real-world entities with properties.

const person = {
  name: "Alice",
  age: 25,
  city: "NYC"
};

console.log(person.name);  // "Alice"
console.log(person.age);   // 25

Creating Objects

// Object literal (most common)
const user = {
  name: "Bob",
  email: "bob@example.com"
};

// Empty object
const empty = {};

// Object constructor (rarely used)
const obj = new Object();
obj.name = "Alice";

Accessing Properties

const person = {
  name: "Alice",
  age: 25,
  "favorite color": "blue"  // property with space
};

// Dot notation (preferred)
console.log(person.name);  // "Alice"

// Bracket notation (for dynamic or special keys)
console.log(person["age"]);            // 25
console.log(person["favorite color"]); // "blue"

// Dynamic access
const key = "name";
console.log(person[key]);  // "Alice"

Adding and Modifying Properties

const person = {
  name: "Alice"
};

// Add new property
person.age = 25;
person.city = "NYC";

// Modify existing property
person.name = "Bob";

// Delete property
delete person.city;

console.log(person);  // { name: "Bob", age: 25 }

Methods

Objects can have functions as properties:

const person = {
  name: "Alice",
  age: 25,

  // Method — function as property
  greet: function() {
    console.log(`Hi, I'm ${this.name}`);
  },

  // Shorthand method syntax (modern)
  celebrate() {
    this.age++;
    console.log(`Happy birthday! Now ${this.age}`);
  }
};

person.greet();      // "Hi, I'm Alice"
person.celebrate();  // "Happy birthday! Now 26"

The this Keyword

this refers to the object the method belongs to:

const user = {
  name: "Bob",
  greet() {
    console.log(`Hello, ${this.name}`);
  }
};

user.greet();  // "Hello, Bob"

// Arrow functions don't have their own `this`
const user2 = {
  name: "Alice",
  greet: () => {
    console.log(this.name);  // ❌ doesn't work — `this` is not user2
  }
};

// Use regular functions for methods

Object Destructuring

Extract properties into variables:

const person = {
  name: "Alice",
  age: 25,
  city: "NYC"
};

// Old way
const name = person.name;
const age = person.age;

// Destructuring
const { name, age, city } = person;
console.log(name);  // "Alice"
console.log(age);   // 25

// Rename during destructuring
const { name: personName, age: personAge } = person;
console.log(personName);  // "Alice"

// Default values
const { name, country = "USA" } = person;
console.log(country);  // "USA" (fallback)

// Rest operator
const { name, ...rest } = person;
console.log(rest);  // { age: 25, city: "NYC" }

Spread Operator

Copy and merge objects:

const person = {
  name: "Alice",
  age: 25
};

// Copy object
const copy = { ...person };
console.log(copy);  // { name: "Alice", age: 25 }

// Merge objects
const details = {
  city: "NYC",
  country: "USA"
};

const full = { ...person, ...details };
console.log(full);
// { name: "Alice", age: 25, city: "NYC", country: "USA" }

// Override properties
const updated = {
  ...person,
  age: 26,
  city: "LA"
};
console.log(updated);
// { name: "Alice", age: 26, city: "LA" }

Object Methods

const person = {
  name: "Alice",
  age: 25,
  city: "NYC"
};

// Get all keys
const keys = Object.keys(person);
console.log(keys);  // ["name", "age", "city"]

// Get all values
const values = Object.values(person);
console.log(values);  // ["Alice", 25, "NYC"]

// Get key-value pairs
const entries = Object.entries(person);
console.log(entries);
// [["name", "Alice"], ["age", 25], ["city", "NYC"]]

// Check if property exists
console.log("name" in person);  // true
console.log("email" in person); // false

// hasOwnProperty (safer)
console.log(person.hasOwnProperty("name"));  // true

Iterating Over Objects

const person = {
  name: "Alice",
  age: 25,
  city: "NYC"
};

// for...in loop
for (const key in person) {
  console.log(`${key}: ${person[key]}`);
}

// Object.entries with for...of (modern)
for (const [key, value] of Object.entries(person)) {
  console.log(`${key}: ${value}`);
}

// Object.keys + forEach
Object.keys(person).forEach((key) => {
  console.log(`${key}: ${person[key]}`);
});

Nested Objects

Objects can contain other objects:

const user = {
  name: "Alice",
  address: {
    street: "123 Main St",
    city: "NYC",
    zip: "10001"
  },
  hobbies: ["reading", "coding"]
};

// Access nested properties
console.log(user.address.city);  // "NYC"
console.log(user.hobbies[0]);    // "reading"

// Optional chaining — safe access to nested properties
console.log(user.address?.city);        // "NYC"
console.log(user.phone?.number);        // undefined (no error)
console.log(user.hobbies?.[0]);         // "reading"

// Destructuring nested objects
const { address: { city, zip } } = user;
console.log(city);  // "NYC"
console.log(zip);   // "10001"

Computed Property Names

Create properties dynamically:

const key = "name";
const value = "Alice";

// Dynamic property
const person = {
  [key]: value
};
console.log(person);  // { name: "Alice" }

// With expressions
const prefix = "user";
const obj = {
  [`${prefix}Name`]: "Bob",
  [`${prefix}Age`]: 30
};
console.log(obj);  // { userName: "Bob", userAge: 30 }

Shorthand Property Names

If variable name matches property name:

const name = "Alice";
const age = 25;

// Old way
const person = {
  name: name,
  age: age
};

// Shorthand
const person = {
  name,
  age
};
console.log(person);  // { name: "Alice", age: 25 }

Common Patterns

// Merge with defaults
function createUser(options) {
  const defaults = {
    role: "user",
    active: true
  };

  return { ...defaults, ...options };
}

const user = createUser({ name: "Alice", role: "admin" });
console.log(user);
// { role: "admin", active: true, name: "Alice" }

// Transform object values
const prices = { apple: 1, banana: 2, orange: 3 };
const doubled = Object.fromEntries(
  Object.entries(prices).map(([key, value]) => [key, value * 2])
);
console.log(doubled);
// { apple: 2, banana: 4, orange: 6 }

// Filter object properties
const user = {
  name: "Alice",
  password: "secret",
  email: "alice@example.com"
};

const safe = Object.fromEntries(
  Object.entries(user).filter(([key]) => key !== "password")
);
console.log(safe);
// { name: "Alice", email: "alice@example.com" }

Key Takeaways

  • Objects store key-value pairs
  • Use dot notation when possible, bracket notation for dynamic access
  • Methods are functions stored as object properties
  • Use regular functions for methods (not arrow functions) to access this
  • Spread operator (...) copies and merges objects
  • Destructuring extracts properties into variables
  • Object.keys(), Object.values(), Object.entries() help iterate over objects
  • Optional chaining (?.) safely accesses nested properties