How to correctly clone a JavaScript object?

Introduction

In JavaScript, when you need to create a copy of an object, you might be tempted to use the assignment operator (=) to assign the values of one object to another. However, this only creates a shallow copy, meaning that if the original object is modified, the copied object will also reflect those changes. To create a true copy of an object, you need to clone it. In this article, we will explore various methods to correctly clone a JavaScript object.

Methods to Clone JavaScript Objects

1. Method: Using the spread operator (ES6)

The spread operator (three dots: ...) can be used to create a shallow copy of an object by copying all enumerable properties from the source object to the target object. However, it does not create a deep copy of nested objects or arrays.


            const obj1 = { name: 'John', age: 30 };
            const obj2 = { ...obj1 };
            console.log(obj2);  // { name: 'John', age: 30 }

            obj2.name = 'Jane';
            console.log(obj1);  // { name: 'John', age: 30 }
            console.log(obj2);  // { name: 'Jane', age: 30 }
        

2. Method: Using Object.assign()

The Object.assign() method can be used to copy enumerable properties from one or more source objects to a target object. It creates a shallow copy of the object.


            const obj1 = { name: 'John', age: 30 };
            const obj2 = Object.assign({}, obj1);
            console.log(obj2);  // { name: 'John', age: 30 }

            obj2.name = 'Jane';
            console.log(obj1);  // { name: 'John', age: 30 }
            console.log(obj2);  // { name: 'Jane', age: 30 }
        

3. Method: Using JSON.parse() and JSON.stringify()

You can clone a JavaScript object by converting it to a JSON string using JSON.stringify(), and then parsing the string to create a new object using JSON.parse(). This method creates a deep copy of the object, including nested objects and arrays. However, it will not preserve functions or symbols.


            const obj1 = { name: 'John', age: 30 };
            const obj2 = JSON.parse(JSON.stringify(obj1));
            console.log(obj2);  // { name: 'John', age: 30 }

            obj2.name = 'Jane';
            console.log(obj1);  // { name: 'John', age: 30 }
            console.log(obj2);  // { name: 'Jane', age: 30 }
        

4. Method: Using a custom cloning function

If you want more control over the cloning process, you can create a custom cloning function. This function can iterate over the properties of the source object using a loop, and copy each property to the target object. This method allows you to handle edge cases, such as circular references, more easily.


            function cloneObject(obj) {
                const clone = {};
                for (let key in obj) {
                    if (obj.hasOwnProperty(key)) {
                        clone[key] = obj[key];
                    }
                }
                return clone;
            }

            const obj1 = { name: 'John', age: 30 };
            const obj2 = cloneObject(obj1);
            console.log(obj2);  // { name: 'John', age: 30 }

            obj2.name = 'Jane';
            console.log(obj1);  // { name: 'John', age: 30 }
            console.log(obj2);  // { name: 'Jane', age: 30 }
        

5. Method: Using the Object.create() method

The Object.create() method can be used to create a new object with the specified prototype object and properties. When using this method to clone an object, you need to pass the original object as an argument to create a new object with the same prototype. However, this method only creates a shallow copy of the object.


            const obj1 = { name: 'John', age: 30 };
            const obj2 = Object.create(Object.getPrototypeOf(obj1), Object.getOwnPropertyDescriptors(obj1));
            console.log(obj2);  // { name: 'John', age: 30 }

            obj2.name = 'Jane';
            console.log(obj1);  // { name: 'John', age: 30 }
            console.log(obj2);  // { name: 'Jane', age: 30 }
        

6. Method: Using a library or utility function

There are several libraries and utility functions available that provide comprehensive cloning features, including deep cloning and handling of complex data structures. Some popular options are Lodash's cloneDeep() function and jQuery's extend() method. These libraries can simplify the cloning process and handle more advanced object structures.


            const obj1 = { name: 'John', age: 30 };
            const obj2 = _.cloneDeep(obj1); // Using Lodash

            obj2.name = 'Jane';
            console.log(obj1);  // { name: 'John', age: 30 }
            console.log(obj2);  // { name: 'Jane', age: 30 }
        

Conclusion

Cloning JavaScript objects correctly is essential to avoid unintended side effects and preserve the integrity of the original object. Depending on your requirements, you can use different methods to clone objects in JavaScript. The choice of method depends on whether you need a shallow or deep copy, and whether you require additional features such as preserving functions or handling circular references.

By using the spread operator, Object.assign(), JSON.parse() and JSON.stringify(), custom cloning functions, or libraries/utilities like Lodash or jQuery, you can clone JavaScript objects effectively and efficiently.