How to Copy Objects in JavaScript: What Are Shallow and Deep Copies and How Can You Achieve Them?

How to Copy Objects in JavaScript: What Are Shallow and Deep Copies and How Can You Achieve Them?

Copying Objects in JavaScript: Shallow and Deep Copy Explained

In JavaScript, when dealing with objects, it is often necessary to create copies of an object. However, copying objects isn’t as simple as copying primitive data types like numbers or strings. This is because objects in JavaScript are reference types. When an object is assigned to a variable, that variable holds a reference (or pointer) to the object, not the object itself.

There are two main types of copying in JavaScript:

  1. Shallow Copy

  2. Deep Copy

1. Shallow Copy in JavaScript

A shallow copy creates a new object, but it only copies the top-level properties of the original object. If there are nested objects, only references to those objects are copied, meaning both the original and copied objects share the same nested objects. Therefore, changes made to nested objects in either the original or the copied object will reflect in both the object.

Methods to Achieve a Shallow Copy:

  • Using Object.assign()

  • Using the spread operator ...

  • Using Lodash’s Library _.clone() Method

Example 1: Shallow Copy Using Object.assign()

let dog = {
    name: 'Tommy',
    breed: 'Labrador',
    details: {
        age: 3,
        address: 'Delhi'
    }
};

// Creating a shallow copy of 'dog' using Object.assign()
let dogCopy = Object.assign({}, dog);

// Modifying properties in the original object
dog.name = 'Charlie'; // Changes the original object
dog.details.age = 4;  // Modifies the nested object

console.log(dog);     // { name: 'Charlie', breed: 'Labrador', details: { age: 4, address: 'Delhi' } }
console.log(dogCopy); // { name: 'Tommy', breed: 'Labrador', details: { age: 4, address: 'Delhi' } }

Explanation:

  • The shallow copy only copies the top-level properties of the object dog.
  • The details object is still shared between dog and dogCopy, so changing the nested object details.age in dog also reflects in dogCopy also.

Example 2: Shallow Copy Example Using Spread Operator (...)

let dog = {
    name: 'Tommy',
    breed: 'Labrador',
    details: {
        age: 3,
        address: 'Delhi'
    }
};

// Creating a shallow copy of 'dog' using the spread operator
let dogCopy = { ...dog };

// Modifying properties in the copied object
dog.name = 'Charlie'; // Original object modified
dog.details.age = 4;  // Nested object modified

console.log(dog);     // { name: 'Charlie', breed: 'Labrador', details: { age: 4, address: 'Delhi' } }
console.log(dogCopy); // { name: 'Tommy', breed: 'Labrador', details: { age: 4, address: 'Delhi' } }

Explanation:

  • Like Object.assign(), the spread operator creates a shallow copy of the top-level properties.

  • The details object is still shared between dog and dogCopy, so changing the nested object details.age in dog also reflects in dogCopy also.

Example 3: Shallow Copy Example Using Lodash’s Library _.clone() Method

To use Lodash in your JavaScript project, you first need to install it. If you’re working in a Node.js environment or using package managers like npm or yarn, you can install Lodash with the following commands:

  • Using npm:
npm install lodash
  • Using yarn:
yarn install lodash

After installing Lodash, you can require it in your JavaScript file and start using its various utility functions, including _.clone() for shallow copying.

// Require the lodash library
const _ = require('lodash');

// Original object - 'dog'
let dog = {
    name: 'Tommy',
    breed: 'Labrador',
    details: {
        age: 3,
        address: 'Delhi'
    }
};

// Creating a shallow copy of 'dog' using _.clone()
let dogCopy = _.clone(dog);

// Modifying properties in the copied object
dog.name = 'Charlie'; // Original object modified
dog.details.age = 4;  // Nested object modified

console.log(dog);     // { name: 'Charlie', breed: 'Labrador', details: { age: 4, address: 'Delhi' } }
console.log(dogCopy); // { name: 'Tommy', breed: 'Labrador', details: { age: 4, address: 'Delhi' } }

Explanation:

  • Lodash’s _.clone() creates a shallow copy of the object, meaning only the top-level properties are copied. In this case, properties like name and breed are copied directly.

  • The details object is still shared between dog and dogCopy, so changing the nested object details.age in dog also reflects in dogCopy also.

  • Lodash’s _.clone() is similar to the Object.assign() and the spread operator (...) when it comes to shallow copying, as it doesn't handle nested objects deeply.

2. Deep Copy in JavaScript

A deep copy creates a complete, independent copy of the original object, including all nested objects. Modifying the deep copy does not affect the original object, and vice versa. In other words, the deep copy replicates the entire structure of the object.

Methods to Achieve a Deep Copy:

  • Using JSON.parse(JSON.stringify(object))

  • Manually using recursive functions

  • Using Lodash’s Library _.cloneDeep() Method

Example 1: Deep Copy Using JSON.parse(JSON.stringify(object))

How it works?

  • Convert to JSON String: JSON.stringify(object) takes the original object and converts it into a JSON string, copying all its properties, including nested objects.

  • Parse Back to Object: JSON.parse(jsonString) takes the JSON string and converts it back into a new JavaScript object. This new object is independent of the original, meaning changes to one do not affect the other.

let dog = {
    name: 'Tommy',
    breed: 'Labrador',
    details: {
        age: 3,
        address: 'Delhi'
    }
};

// Creating a deep copy using JSON.parse(JSON.stringify())
let dogCopy = JSON.parse(JSON.stringify(dog));

// Modifying properties in the original object
dog.name = 'Charlie'; // Original object modified
dog.details.age = 4;  // Nested object modified

console.log(dog);     // { name: 'Charlie', breed: 'Labrador', details: { age: 4, address: 'Delhi' } }
console.log(dogCopy); // { name: 'Tommy', breed: 'Labrador', details: { age: 3, address: 'Delhi' } }

Explanation:

  • JSON.parse(JSON.stringify(dog)) creates a deep copy of the dog object.

  • Changes to the original dog object do not affect dogCopy.

Example 2: Deep Copy Using a Recursive Function

let dog = {
    name: 'Tommy',
    breed: 'Labrador',
    details: {
        age: 3,
        address: 'Delhi'
    }
};

// Recursive function to create a deep copy of an object
function deepCopy(obj) {
    // Initialize an empty object to hold the copy
    let copy = {};

    // Iterate over each key in the original object
    for (let key in obj) {
        // Check if the property is an object and not null (to avoid copying null values)
        if (typeof obj[key] === 'object' && obj[key] !== null) {
            // Recursively call deepCopy for nested objects and assign the result to the corresponding key in the copy
            copy[key] = deepCopy(obj[key]); 
        } else {
            // Copy primitive values directly to the new object
            copy[key] = obj[key]; 
        }
    }

    // Return the deep copy of the object
    return copy;
}

// Create a deep copy of the original dog object
let dogCopy = deepCopy(dog);

// Modifying properties in the original object
dog.name = 'Charlie'; // Original object modified
dog.details.age = 4;  // Nested object modified

console.log(dog);     // { name: 'Charlie', breed: 'Labrador', details: { age: 4, address: 'Delhi' } }
console.log(dogCopy); // { name: 'Tommy', breed: 'Labrador', details: { age: 3, address: 'Delhi' } }

Explanation:

  • The deepCopy() function recursively copies each property of the dog object.

  • Changes to the original dog object do not affect dogCopy.

Example 3: Deep Copy Using Lodash’s Library _.cloneDeep() Method

To use Lodash in your JavaScript project, you first need to install it. If you’re working in a Node.js environment or using package managers like npm or yarn, you can install Lodash with the following commands:

  • Using npm:
npm install lodash
  • Using yarn:
yarn install lodash

After installing Lodash, you can require it in your JavaScript file and start using its various utility functions, including _.cloneDeep() for shallow copying.

const _ = require('lodash');

let dog = {
    name: 'Tommy',
    breed: 'Labrador',
    details: {
        age: 3,
        address: 'Delhi'
    }
};

// Creating a deep copy using lodash's cloneDeep()
let dogCopy = _.cloneDeep(dog);

// Modifying properties in the original object
dog.name = 'Charlie'; // Original object modified
dog.details.age = 4;  // Nested object modified

console.log(dog);     // { name: 'Charlie', breed: 'Labrador', details: { age: 4, address: 'Delhi' } }
console.log(dogCopy); // { name: 'Tommy', breed: 'Labrador', details: { age: 3, address: 'Delhi' } }

Explanation:

  • _.cloneDeep(dog) creates a deep copy of the dog object, including all nested properties.

  • Changes to the original dog object do not affect dogCopy.

Summary of Key Differences Between Shallow Copy and Deep Copy: