Advanced Object Concepts

  • This lesson covers advanced object-oriented concepts in JavaScript.
  • Introduction to Advanced Object Concepts

    In earlier lessons, we learned:

    • What objects are

    • How to create and access objects

    • Object properties and methods

    As applications grow larger, how we handle objects becomes extremely important.

    Poor object handling can lead to:

    • Bugs

    • Unexpected data changes

    • Difficult debugging

    • Poor code quality

    In this lesson, we will cover advanced and professional object concepts that every JavaScript developer must know.

    Topics covered:

    1. Shallow vs Deep Copy

    2. Spread Operator with Objects

    3. Immutability in Objects

    4. Clean Object Design

    Shallow vs Deep Copy

    What is Copying an Object ?

    Copying an object means creating a new object based on an existing object.

    There are two types of copying:

    • Shallow Copy

    • Deep Copy

    Understanding the difference is critical.

    Shallow Copy

    Definition

    A shallow copy copies only the top-level properties of an object.

    If the object contains nested objects, the nested object is shared, not duplicated.

Shallow Copy using Spread Operator

Copies top-level properties while nested objects remain shared references.

// Shallow copy example

let user = {
  name: "Amit",
  address: {
    city: "Delhi"
  }
};

let copyUser = { ...user };

copyUser.name = "Rahul";            // changes only copy
copyUser.address.city = "Mumbai";   // affects original (shared reference)

console.log(user.name);         // Amit
console.log(user.address.city); // Mumbai
  • Explanation

    • name is copied independently

    • address is a reference to the same object

    • Changing nested data affects both objects

    This is the main drawback of shallow copy.

    Ways to Create Shallow Copy

    • Spread operator { ...obj }

    • Object.assign()

Ways to Create Shallow Copy

Uses spread operator or Object.assign to copy top-level properties only.

// Using Object.assign for shallow copy

let oldObj = {
  name: "Amit",
  address: {
    city: "Delhi"
  }
};

let newObj = Object.assign({}, oldObj);

// Modify values
newObj.name = "Rahul";
newObj.address.city = "Mumbai"; // affects original (shared reference)

console.log(oldObj.name);         // Amit
console.log(oldObj.address.city); // Mumbai
  • Deep Copy

    Definition

    A deep copy creates a completely independent copy, including all nested objects.

    Changes in the copied object do not affect the original object.

Deep Copy using JSON Method

Creates a fully independent copy including nested objects.

// Deep copy example

let user = {
  name: "Amit",
  address: {
    city: "Delhi"
  }
};

let deepCopy = JSON.parse(JSON.stringify(user));

deepCopy.address.city = "Mumbai";

console.log(user.address.city);     // Delhi
console.log(deepCopy.address.city); // Mumbai
  • Explanation

    • Entire object structure is duplicated

    • No shared references

    • Safer for complex data

    Limitations of JSON Deep Copy

    • Cannot copy functions

    • Loses undefined values

    • Not suitable for Date, Map, Set

      Shallow vs Deep Copy

      FeatureShallow CopyDeep Copy
      Nested objectsSharedFully copied
      SafetyRiskySafe
      PerformanceFasterSlower
      Use caseSimple objectsComplex objects

      Spread Operator with Objects

      What is Spread Operator ?

      The spread operator (...) expands object properties into a new object.

      It is widely used for:

      • Copying objects

      • Merging objects

      • Updating object properties

    Basic Object Copy with Spread

    Creates a new object by copying properties using the spread operator.

    // Copy object using spread
    
    let user = {
      name: "Amit",
      age: 25
    };
    
    let newUser = { ...user };
    
    console.log(newUser);
    • This creates a shallow copy.

    Updating Object with Spread

    Overrides specific properties while copying the rest of the object.

    // Updating properties using spread
    
    let user = {
      name: "Amit",
      age: 25
    };
    
    let updatedUser = {
      ...user,
      age: 26
    };
    
    console.log(updatedUser); // { name: "Amit", age: 26 }
    • Original object remains unchanged.

      Merging Objects Using Spread

    Merging Objects with Spread

    Combines multiple objects into a single object using spread syntax.

    // Merging objects using spread
    
    let personal = { name: "Amit" };
    let contact = { email: "amit@gmail.com" };
    
    let profile = { ...personal, ...contact };
    
    console.log(profile); // { name: "Amit", email: "amit@gmail.com" }
    • Spread Operator with Nested Objects

    Shallow Copy with Nested Objects

    Copies object but nested objects remain shared references.

    // Spread with nested object (shallow copy)
    
    let user = {
      name: "Amit",
      address: {
        city: "Delhi"
      }
    };
    
    let copy = { ...user };
    
    // Changing nested value
    copy.address.city = "Mumbai";
    
    console.log(user.address.city); // Mumbai (same reference)
    • This is still a shallow copy.

      Nested objects are shared.

      Immutability in Objects

      What is Immutability ?

      Immutability means not changing the original object, but instead creating a new modified copy.

      Why is Immutability Important ?

      • Prevents unexpected bugs

      • Makes debugging easier

      • Improves code predictability

      • Essential in modern frameworks

    Mutable vs Immutable Object Update

    Shows bad (direct mutation) vs good (creating new object) practice

    // Mutable Example (Bad Practice)
    let user = { name: "Amit" };
    
    user.name = "Rahul"; // original object modified
    
    
    // Immutable Example (Good Practice)
    let user = { name: "Amit" };
    
    let newUser = { ...user, name: "Rahul" }; // new object created
    • Original object remains unchanged.

    Immutability with Nested Objects

    Demonstrates updating nested objects without modifying the original object

    let user = {
      name: "Amit",
      address: {
        city: "Delhi"
      }
    };
    
    let updatedUser = {
      ...user,
      address: {
        ...user.address,
        city: "Mumbai"
      }
    };
    • This ensures full immutability.

      Benefits of Immutability

      • Easy change tracking

      • Reliable state management

      • Better testing

      • Cleaner architecture

      Clean Object Design

      What is Clean Object Design ?

      Clean object design means creating objects that are:

      • Easy to read

      • Easy to maintain

      • Easy to extend

      • Free from unnecessary complexity

        Principles of Clean Object Design

        Use Meaningful Property Names

      Clean Object Design with Meaningful Property Names

      Improves code readability by using clear and descriptive property names

      // Bad
      let u = { n: "Amit" };
      
      // Good
      let user = { name: "Amit" };
      • Keep Objects Focused (Single Responsibility)

      Keep Objects Focused with Single Responsibility Principle

      Ensures each object handles only one responsibility for better maintainability

      // Bad
      let user = {
        name: "Amit",
        calculateSalary() {},
        sendEmail() {}
      };
      
      // Good
      let user = { name: "Amit" };
      let salaryService = {};
      let emailService = {};
      • Avoid Deep Nesting

      Reduce Complexity by Flattening Object Structure

      Makes code easier to read by avoiding deeply nested object paths

      // Bad
      user.data.profile.details.address.city
      • Prefer simpler structure where possible.

        Use Consistent Structure

        All objects of the same type should follow the same structure.

        Do Not Mutate Shared Objects

        Always use immutable updates for shared data.

      Updating Nested Object Data Without Mutation

      Demonstrates a clean way to update nested properties using immutability

      function updateUserCity(user, newCity) {
        return {
          ...user,
          address: {
            ...user.address,
            city: newCity
          }
        };
      }
      • This approach is:

        • Safe

        • Predictable

        • Reusable

        Common Mistakes

        • Confusing shallow copy with deep copy

        • Accidentally mutating objects

        • Overusing deep nesting

        • Using unclear property names

        Best Practices Summary

        • Prefer immutability

        • Understand copy types clearly

        • Use spread operator carefully

        • Keep objects clean and focused

        • Avoid unnecessary mutations