How To Clone An Object In JavaScript

There are several methods in JavaScript to clone an object like for shallow cloning you can use Object.assign() method or Spread Operator (), and for deep cloning you can use JSON.parse() with JSON.stringify() or custom recursive function or external libraries.

Table of Contents #
  1. Understanding Object Cloning
  2. Shallow Cloning Methods
  3. Deep Cloning Methods
  4. External Libraries
  5. Choosing the Right Cloning Method

1. Understanding Object Cloning

Object cloning refers to the process of creating a duplicate of an existing object. Cloning can be shallow or deep, depending on whether nested objects are also copied. Shallow cloning creates a new object with references to the same nested objects, while deep cloning creates entirely new copies of nested objects.

2. Shallow Cloning Methods

Using Object.assign() Method:

const clonedObj = Object.assign({}, originalObj);
// Original object with a nested user object and a sayHi method
const originalObj = {
    user: {
        name: 'Rahul',
        age: 24,
    },
    sayHi: function () {
        console.log(this.name);
    },
};

// Shallow clone of the original object using Object.assign
const clonedObj = Object.assign({}, originalObj);

// Modifying the name property of the nested user object in the cloned object
clonedObj.user.name = 'John';

console.log(originalObj);
console.log(clonedObj);

Output:

{ user: { name: 'John', age: 24 }, sayHi: [Function: sayHi] }
{ user: { name: 'John', age: 24 }, sayHi: [Function: sayHi] }

Object.assign() copies enumerable own properties from one or more source objects to a target object. However, it only performs a shallow copy.

Using Spread Operator (…):

const clonedObj = { ...originalObj };
// Original object with a nested user object and a sayHi method
const originalObj = {
    user: {
        name: 'Rahul',
        age: 24,
    },
    sayHi: function () {
        console.log(this.name);
    },
};

// Shallow clone of the original object using spread operator
const clonedObj = { ...originalObj };

// Modifying the name property of the nested user object in the cloned object
clonedObj.user.name = 'John';

console.log(originalObj);
console.log(clonedObj);

The spread operator offers a concise syntax for creating shallow copies of objects. It spreads the properties of the original object into a new object.

3. Deep Cloning Methods

JSON.parse() and JSON.stringify():

const clonedObj = JSON.parse(JSON.stringify(originalObj));
// Original object with a nested user object and a sayHi method
const originalObj = {
    user: {
        name: 'Rahul',
        age: 24,
    },
    sayHi: function () {
        console.log(this.name);
    },
};

// Deep clone of the original object using JSON methods
const clonedObj = JSON.parse(JSON.stringify(originalObj));

// Modifying the name property of the nested user object in the cloned object
clonedObj.user.name = 'John';

console.log(originalObj);
console.log(clonedObj);

Output:

{ user: { name: 'Rahul', age: 24 }, sayHi: [Function: sayHi] }
{ user: { name: 'John', age: 24 } }

Using JSON.stringify() serializes an object into a JSON string, and JSON.parse() parses the JSON string back into an object. This approach effectively performs deep cloning, but it has limitations with certain data types such as functions and undefined values.

Recursive Deep Clone:

// Function to deeply clone an object
function deepClone(obj) {
    // Base case: if obj is not an object or is null, return obj itself
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
    // Create an empty array or object based on obj's type
    const cloned = Array.isArray(obj) ? [] : {};
    // Traverse each key in obj
    for (let key in obj) {
        // Check if the key belongs to obj itself (not inherited)
        if (obj.hasOwnProperty(key)) {
            // Recursively clone nested objects or arrays
            cloned[key] = deepClone(obj[key]);
        }
    }
    // Return the deeply cloned object
    return cloned;
}

// Deep clone the original object
const clonedObj = deepClone(originalObj);
// Original object with a nested user object containing properties and an array
const originalObj = {
    user: {
        name: 'Rahul',
        age: 24,
        item: [1, [1, 2, 3], 3],
    },
    sayHi: function () {
        console.log(this.name);
    },
};

function deepClone(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
    const cloned = Array.isArray(obj) ? [] : {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            cloned[key] = deepClone(obj[key]);
        }
    }
    return cloned;
}

const clonedObj = deepClone(originalObj);

// Modifying the nested array in the cloned object
clonedObj.user.item[1].push(5);

// Logging the nested array in both original and cloned objects
console.log(originalObj.user.item); // Output: [1, [1, 2, 3], 3]
console.log(clonedObj.user.item); // Output: [1, [1, 2, 3, 5], 3]

A recursive deep clone function traverses the entire object hierarchy, creating new copies of nested objects and arrays. This method is more robust than JSON-based cloning but can be less performant for very large objects.

4. External Libraries

Several third-party libraries such as lodash (_.cloneDeep(value)) provide utility functions for object cloning, including deep cloning methods. These libraries offer comprehensive solutions with additional features and optimizations.

5. Choosing the Right Cloning Method

  • Use shallow cloning for simple objects or when maintaining references to nested objects is acceptable.
  • Prefer deep cloning when you need to create independent copies of nested objects or arrays.
  • Consider the limitations and performance implications of each cloning method, especially for large or complex objects.