Table of Contents
Understanding JavaScript Object Mechanisms
DeepCopy VS ShallowCopy
Shallow Copy
Deep Copy
Methods to Copy Objects
Shallow Copy Using the Spread Operator (...)
Shallow Copy Using Object.assign()
Deep Copy Using JSON.parse(JSON.stringify(obj))
Deep Copy Using a Custom Function
Deep Copy Using Lodash's _.cloneDeep() Method
Using JavaScript's Native structuredClone() Method
Note: In javascript everything (wwell almost everything! ) is an object.
I am talking about copying an object that includes, strings, arrays, objects, functions, and other data types except null and undefined.
But why do we need to copy an object in Javascript, one may ask?
let obj1 = {
a : "1234"
}
let obj2 = obj1
obj1.a // "1234"
obj2.a // "1234"
Here, we already had an obj1
which has a property a
we want to have this exact value to be stored in another object say obj2
. Now the simplest thing to do is to an assignment operation obj2 = obj1
now obj2
also has a
a property in it.
but, let's change the value obj1.a
to something else and see how it affects the obj2
.
consider :
let obj1 = {
a : "1234"
}
let obj2 = obj1
obj1.a // "1234"
obj2.a // "1234"
obj1.a = "something else"
Actual behavior
obj1.a // "something else"
obje2.a // "something else"
That's might seem like a weird side-effect to you but it just means you don't yet understand how javascript stores the values in Input. Let's understand it in the further article.
Understanding Javascript Object Mechanisms
To understand the above behavior you need to understand how Javascript Objects work and how the values are stored in the Javascript. Well, Javascript does not store the exact value but instead, it contains a reference to the memory where the value is stored.
Does it make sense? If not below illustration may help you to understand better.
As in Above Diagram you can see in MyObject.a
and MyObject.b
have a value stored where values are Object1
and arr1
. But In actuality, the value is not stored instead the reference to memory is saved.
So in our previous example when we changed the value of obj1.a
to something else the value is changed but obj2
still referred to obj1
hence it changes with it.
DeepCopy VS ShallowCopy
Let's understand these two ways to fully understand how we can finally be able to understand how to copy an object in Javascript.
Shallow Copy
A shallow copy of an object only creates a new reference to the same object, not a completely new object. This means that if you modify a property of the shallow copy, the original object will also be modified because both objects reference the same underlying object.
Example:
let originalObj = {a: 1, b: {c: 2}};
let shallowCopy = Object.assign({}, originalObj);
console.log(originalObj.b === shallowCopy.b) // outputs: true
shallowCopy.b.c = 3;
console.log(originalObj.b.c) // outputs: 3
Deep Copy
A deep copy, on the other hand, is a copy of an object that is completely independent of the original object. Changes made to the properties of the deep copy will not affect the properties of the original object.
Example:
let originalObj = {a: 1, b: {c: 2}};
let deepCopy = JSON.parse(JSON.stringify(originalObj));
console.log(originalObj.b === deepCopy.b) // outputs: false
deepCopy.b.c = 3;
console.log(originalObj.b.c) // outputs: 2
Note: Using the above method may cause some problems with Date Object
as it's a Javascript Object.
Hence while deep copying it is advised to use the popular OSS library's lodash _.cloneDeep()
method or write a program that recursively does copy in each level of a nested Javascript Object.
Methods to copy Objects.
In JavaScript, there are several methods to copy an object:
Shallow copy using the spread operator
...
goCopy codeconst original = { a: 1, b: 2 };
const copy = { ...original };
Pros:
Easy to use
Works with all enumerable properties, including arrays and objects
Cons:
- Only creates a shallow copy, meaning that any nested objects or arrays are still referenced and not copied.
Shallow copy using Object.assign()
javascriptCopy codeconst original = { a: 1, b: 2 };
const copy = Object.assign({}, original);
Pros:
Easy to use
Works with all enumerable properties, including arrays and objects
Cons:
- Only creates a shallow copy, meaning that any nested objects or arrays are still referenced and not copied
Deep copy using
JSON.parse(JSON.stringify(obj))
javascriptCopy codeconst original = { a: 1, b: 2, c: { d: 3 } };
const copy = JSON.parse(JSON.stringify(original));
Pros:
Easy to use
Creates a deep copy, meaning that any nested objects or arrays are also copied
Cons:
Will not work for objects with circular references or for non-JSON data types (e.g. functions, symbol properties)
Can be slow for large objects
Deep copy using a custom function
scssCopy codefunction deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
let copy;
if (Array.isArray(obj)) {
copy = [];
for (let i = 0; i < obj.length; i++) {
copy[i] = deepCopy(obj[i]);
}
} else {
copy = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]);
}
}
}
return copy;
}
Pros:
Creates a deep copy, meaning that any nested objects or arrays are also copied
Can handle objects with circular references or non-JSON data types
Cons:
More complicated to implement
Slower for large objects compared to JSON.parse(JSON.stringify(obj))
Deep copy using Lodash's
_.cloneDeep()
method
javascriptCopy codeconst _ = require('lodash');
const original = { a: 1, b: 2, c: { d: 3 } };
const copy = _.cloneDeep(original);
Pros:
Easy to use
Creates a deep copy, meaning that any nested objects or arrays are also copied
Can handle objects with circular references or non-JSON data types
Generally faster for large objects compared to a custom deep copy function
Cons:
Requires the Lodash library to be installed
Adds extra size to your codebase if you only need this single function.
Using Javascript's Native
structuredClone()
method
// Create an object with a value and a circular reference to itself.
const original = { name: "MDN" };
original.itself = original;
// Clone it
const clone = structuredClone(original);
console.assert(clone !== original); // the objects are not the same (not same identity)
console.assert(clone.name === "MDN"); // they do have the same values
console.assert(clone.itself === clone); // and the circular reference is preserved
Pros :
- Javascript Native Support hence no library is required.
Cons:
- This method is not a part of ECMAScript it was added to the platform-specific parts.
Conclusion
In conclusion, copying an object in JavaScript can be a complex task due to its nature of storing references instead of actual values. Shallow copy is created by methods such as spread operator (...), Object.assign(), and only creates a new reference to the same object, modifying the property in the shallow copy will affect the original object. On the other hand, a deep copy creates a completely independent copy of an object, and changes made to the properties of the deep copy will not affect the properties of the original object. It is advised to use popular libraries like lodash to create a deep copy or to write a program that recursively copies each level of a nested Javascript object.