Clone Objects in JavaScript: Complete Guide with structuredClone, JSON and lodash
Cloning objects is a fundamental task in JavaScript, especially when we work with complex data structures.
What is the best way to do it? In this article, we will explore three popular methods:
structuredCLone
utility: the new native JavaScript API (2022).JSON.parse(JSON.stringify())
.lodash
types of cloning objects.
We will analyze their advantages, disadvantages and how they behave with different types of data. Let us begin!
How we deeply clone an objecy?
Cloning an object means creating an independent copy of the original object. There are two main types of cloning:
Shallow copy
Only primitives (string, number…) values of the original object are copied. If the object contains other objects or nested arrays, these are not copied, but are shared between the original and the copy.
Examples of shallow copy:
Spread syntax
[…] {…}
// Shallow copy with Spread syntax
// Original array
const fruits = ["🍎", "🍌", "🍒"];
// Shallow copy
const copyFruits = [...fruits];
// Add a new element to the shallow copy
copyFruits.push("🍇");
// Print the results
console.log(fruits); // ["🍎", "🍌", "🍒"] (Original is unchanged)
console.log(copyFruits); // ["🍎", "🍌", "🍒", "🍇"] (Shallow copy modified)
Object.assign()
// Shallow Copy with Object.assign()
// Original array
const originalFruits = ["🍎", "🍌", "🍒"];
// Create a shallow copy using Object.assign()
const shallowCopyFruits = Object.assign([], originalFruits);
// Modify the shallow copy
shallowCopyFruits.push("🍇"); // Add grapes
// Output results
console.log("Original:", originalFruits);
console.log("Shallow Copy:", shallowCopyFruits);
// Original: [ '🍎', '🍌', '🍒' ]
// Shallow Copy: [ '🍎', '🍌', '🍒', '🍇' ]
Array.from()
// Shallow Copy with Array.from()
// Original array
const originalFruits = ["🍎", "🍌", "🍒"];
// Create a shallow copy using Array.from()
const shallowCopyFruits = Array.from(originalFruits);
// Modify the shallow copy
shallowCopyFruits.push("🍇"); // Add grapes
// Output results
console.log("Original:", originalFruits);
console.log("Shallow Copy:", shallowCopyFruits);
// Original: [ '🍎', '🍌', '🍒' ]
// Shallow Copy: [ '🍎', '🍌', '🍒', '🍇' ]
Object.create()
// Array-like Object with Object.create()
// Original array
const originalFruits = ["🍎", "🍌", "🍒"];
// Create an empty object with Array.prototype as its prototype
const arrayLikeFruits = Object.create(Array.prototype);
// Copy elements from the original array
for (let i = 0; i < originalFruits.length; i++) {
arrayLikeFruits[i] = originalFruits[i];
}
// Modify the array-like object (push works because of inherited prototype)
arrayLikeFruits.push("🍇"); // Add grapes
// Output results
console.log("Original:", originalFruits);
console.log("Array-Like Object:", arrayLikeFruits);
// Original: [ '🍎', '🍌', '🍒' ]
// Array-Like Object: { '0': '🍎', '1': '🍌', '2': '🍒', '3': '🍇' }
Array.prototype.concat()
// Shallow Copy with Array.prototype.concat()
// Original array
const originalFruits = ["🍎", "🍌", "🍒"];
// Create a shallow copy using concat() (with no arguments to make a copy)
const shallowCopyFruits = originalFruits.concat();
// Modify the shallow copy
shallowCopyFruits.push("🍇"); // Add grapes
// Output results
console.log("Original:", originalFruits);
console.log("Shallow Copy:", shallowCopyFruits);
// Original: [ '🍎', '🍌', '🍒' ]
// Shallow Copy: [ '🍎', '🍌', '🍒', '🍇' ]
In this table you can find all the differences between this methods:
The Problem with Shallow Copies of Nested Objects
A shallow copy creates a new array or object, but when it comes to nested objects within that array or object, it merely copies their references (memory addresses) rather than duplicating the entire nested object.
This means:
The Top-Level is Separate: if you change a primitive value (like the string “JavaScript”) directly in either the original or the copy, the other remains unaffected.
Nested Objects are Linked: if you modify a nested object (like the
{age: ..., creator: ...}
in the example), the change is reflected in both the original and the copy.
Why This Happens
Both the original and the shallow copy end up pointing to the same nested object in memory. Modifying it through one reference affects the other as well.
// Shallow Copy with Spread Syntax
// Original array
const originalFruits = ["🍎", ["🍌", "🍒"]];
// Shallow copy
const shallowCopyFruits = [...originalFruits];
// Modify nested array in the shallow copy
shallowCopyFruits[1][0] = "🍇"; // Replace banana with grapes
// Output results
console.log("Original:", originalFruits);
console.log("Shallow Copy:", shallowCopyFruits);
// Original: [ '🍎', [ '🍇', '🍒' ] ]
// Shallow Copy: [ '🍎', [ '🍇', '🍒' ] ]
How to Avoid This Issue
Deep Copies: Create a deep copy instead. This means you'll make entirely new copies of all nested objects and arrays, breaking the shared references.
Immutable Data: Follow principles of immutability, where you avoid modifying nested objects directly. Instead, create new objects with the desired changes.
// Shallow Copy with Manual Nested Array Creation
// Original array
const originalFruits = ["🍎", ["🍌", "🍒"]];
// Create a new nested array for the copy, not just a reference
const modifiedCopyFruits = [originalFruits[0], [...originalFruits[1]]];
// Modify the nested array in the shallow copy
modifiedCopyFruits[1][0] = "🍇"; // Replace banana with grapes
// Output results
console.log("Original:", originalFruits); // [ '🍎', [ '🍌', '🍒' ] ]
console.log("Shallow Copy:", modifiedCopyFruits); // [ '🍎', [ '🍇', '🍒' ] ]
Deep copy
New copies of all the values of the original object are created, including nested objects and arrays. Changes to the copy do not affect the original.
As we saw with the letter example, shallow copies fail to create independent copies of nested objects or arrays. Deep copies are the answer, recursively duplicating every element and nested structure within your data. This ensures that modifying one copy won’t affect the other.
How can we achieve this:
JSON.parse(JSON.stringify()): This is a simple method for basic objects and arrays. However, it has limitations with functions, circular references, and certain data types like Date
.
// Deep Copy using JSON methods
// Original array
const originalFruits = ["🍎", ["🍌", "🍒"]];
// Deep copy using JSON.parse and JSON.stringify
const deepCopyFruits = JSON.parse(JSON.stringify(originalFruits));
// Modify nested array in the deep copy
deepCopyFruits[1][0] = "🍇"; // Replace banana with grapes
// Output results
console.log("Original:", originalFruits); // [ '🍎', [ '🍌', '🍒' ] ]
console.log("Deep Copy (JSON):", deepCopyFruits); // [ '🍎', [ '🍇', '🍒' ] ]
Structured Clone Algorithm (structuredClone()): This relatively new browser API provides a more robust way to create deep copies, handling a wider range of data types than JSON.parse/stringify
.
// Original array of fruits
const originalFruits = ["🍎", ["🍌", "🍒"]];
// Deep copy using structuredClone
const deepCopyFruits = structuredClone(originalFruits);
// Modify the nested array in the deep copy
deepCopyFruits[1][0] = "🍇"; // Replace banana with grapes
// Output results
console.log("Structured Clone Deep Copy:");
console.log(originalFruits); // ["🍎", ["🍌", "🍒"]] (Unchanged)
console.log(deepCopyFruits); // ["🍎", ["🍇", "🍒"]]
Lodash _.cloneDeep
: For more complex objects, you can use the cloneDeep
function from the Lodash library:
const _ = require('lodash');
// Original array of fruits
const originalFruits = ["🍎", ["🍌", "🍒"]];
// Deep copy using Lodash's _.cloneDeep()
const deepCopyFruits = _.cloneDeep(originalFruits);
// Modify the nested array in the deep copy
deepCopyFruits[1][0] = "🍇"; // Replace banana with grapes
// Output results
console.log("Lodash Deep Copy:");
console.log(originalFruits); // ["🍎", ["🍌", "🍒"]] (Unchanged)
console.log(deepCopyFruits); // ["🍎", ["🍇", "🍒"]]
So you can check the followin table in orde to know when to make a shallow copy or a deep one:
While it’s becoming more widely supported, structuredClone()
might not be available in very old browsers. Check compatibility tables if you need to support those.
Thanks for reading so far 🙏
I’d like to have your feedback so please leave a comment, clap or follow. 👏
Then, if you really liked it share it among your community, tech bros and whoever you want. And don’t forget to follow me on LinkedIn, on Medium or YouTube. 👋😁