Cloning an object means creating an independent copy that can be modified without affecting the original object.
This is especially important because in JavaScript all objects are reference types.
That is, when you assign an object to a new variable, both variables point to the same object in memory. If you modify one, the other will also be affected:
const original = { name: "Juan", age: 25 };
const copy = original;
copy.name = "María";
console.log(original.name); // "María" (the original object also changed)
To avoid unwanted side effects, it is necessary to clone objects instead of just copying the reference.
For this entry, it is very important that you understand what a value type and a reference type are.
Shallow Cloning
Shallow cloning creates a new instance of an object and copies the top-level properties.
However, if any of these properties is an object itself, only the reference will be copied.
Method | Type of cloning | Nested | Special values | Complexity |
---|---|---|---|---|
Spread operator | Shallow | ❌ | ❌ | Low |
Object.assign() | Shallow | ❌ | ❌ | Low |
Using the spread operator ...
The spread operator (...
) is a modern and concise way to clone an object. This method creates a shallow copy of the object, meaning it copies the top-level properties but not nested objects or arrays.
const original = { name: "Juan", age: 25 };
const clone = { ...original };
clone.name = "María";
console.log(original.name); // "Juan" (the original is not modified)
However, if the object has nested properties, they will continue to share references with the original:
const original = { name: "Juan", details: { age: 25, city: "Madrid" } };
const clone = { ...original };
clone.details.city = "Barcelona";
console.log(original.details.city); // "Barcelona" (the original is affected)
Using Object.assign()
Object.assign()
is another way to perform shallow cloning. This method copies the own properties of an object (not inherited ones) to the target object.
const original = { name: "Juan", age: 25 };
const clone = Object.assign({}, original);
clone.name = "María";
console.log(original.name); // "Juan"
Like the spread operator, this method does not clone nested objects.
Deep Cloning
Deep cloning creates a complete copy of an object, including all nested objects.
This ensures that modifications to the copy do not affect the original object (this is “true” cloning).
Method | Type of cloning | Nested | Special values | Complexity |
---|---|---|---|---|
structuredClone() | Deep | ✅ | ✅ | Low |
JSON.parse(JSON.stringify) | Deep | ✅ | ❌ | Medium |
_.cloneDeep() (Lodash) | Deep | ✅ | ✅ | Medium |
Manual cloning | Depends | ✅ | Depends | High |
Using structuredClone()
Starting from ES2021, JavaScript introduced the structuredClone
method, which allows deep cloning natively.
const original = { name: "Juan", details: { age: 25, city: "Madrid" } };
const clone = structuredClone(original);
clone.details.city = "Barcelona";
console.log(original.details.city); // "Madrid"
Advantages
- Handles complex values like
Date
,Map
,Set
, and circular references. - It is faster than JSON-based solutions.
Limitations
- Not available in older versions of JavaScript.
- May not be supported in all browsers.
Using JSON
A simple technique for cloning objects that do not contain methods or non-serializable values (like functions, undefined
, or circular properties) is to use JSON.stringify()
and JSON.parse()
.
const original = { name: "Juan", details: { age: 25, city: "Madrid" } };
const clone = JSON.parse(JSON.stringify(original));
clone.details.city = "Barcelona";
console.log(original.details.city); // "Madrid"
Limitations
- Does not work if the object contains functions,
undefined
values, or circular properties. - Converts special values like
Date
to strings.
Using external libraries
When you need a robust solution for cloning complex objects, you can use libraries like Lodash. The cloneDeep
method from Lodash performs a complete deep clone.
import _ from "lodash";
const original = { name: "Juan", details: { age: 25, city: "Madrid" } };
const clone = _.cloneDeep(original);
clone.details.city = "Barcelona";
console.log(original.details.city); // "Madrid"
Lodash correctly handles objects with circular references and non-serializable types.
Manual Cloning
In certain cases, especially if the object has specific structures or you need to optimize performance, you can clone an object manually by traversing its properties.
Manual shallow cloning
function cloneObject(object) {
const clone = {};
for (let key in object) {
if (object.hasOwnProperty(key)) {
clone[key] = object[key];
}
}
return clone;
}
const original = { name: "Juan", age: 25 };
const clone = cloneObject(original);
clone.name = "María";
console.log(original.name); // "Juan"
Manual deep cloning
function deepClone(object) {
const clone = {};
for (let key in object) {
if (object.hasOwnProperty(key)) {
if (typeof object[key] === "object" && object[key] !== null) {
clone[key] = deepClone(object[key]);
} else {
clone[key] = object[key];
}
}
}
return clone;
}
const original = { name: "Juan", details: { age: 25, city: "Madrid" } };
const clone = deepClone(original);
clone.details.city = "Barcelona";
console.log(original.details.city); // "Madrid"