Cloning an object in JavaScript means creating a new object that has the same properties and values as the original one, but is not the same reference. It’s harder than you think. Writing your own cloning function is not recommended.
There are different ways to clone an object, depending on how deep you want to copy, and how you want to handle reference types, circular references, functions, special objects, hidden properties.
Each method has its pros and cons. Choose it wisely!
...
) to shallow clone an object. This means that only the top-level properties are copied, and any nested objects or arrays will still refer to the original ones.const obj = { a: 1, b: { c: 2 } };
const clone = { ...obj }; // shallow clone
console.log(clone); // { a: 1, b: { c: 2 } }
clone.a = 3; // change clone
console.log(obj.a); // 1, original is not affected
clone.b.c = 4; // change clone
console.log(obj.b.c); // 4, original is affected
Object.assign()
method to shallow clone an object. This works similarly to the spread operator, but allows you to assign multiple sources to a target object.const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const clone = Object.assign({}, obj1, obj2); // shallow clone
console.log(clone); // { a: 1, b: 2, c: 3, d: 4 }
clone.a = 5; // change clone
console.log(obj1.a); // 1, original is not affected
clone.c = 6; // change clone
console.log(obj2.c); // 3, original is not affected
JSON.parse()
and JSON.stringify()
methods to deep clone an object. This means that all the properties and values are copied, including nested objects and arrays. However, this method only works for objects that can be serialized to JSON, which excludes functions, dates, undefined, infinity, regexps, maps, sets, and other complex types.const obj = { a: 1, b: { c: 2 }, d: new Date(), e: function () {} };
const clone = JSON.parse(JSON.stringify(obj)); // deep clone
console.log(clone); // { a: 1, b: { c: 2 }, d: '2023-11-23T01:03:44.148Z' }
clone.a = 7; // change clone
console.log(obj.a); // 1, original is not affected
clone.b.c = 8; // change clone
console.log(obj.b.c); // 2, original is not affected
console.log(clone.d); // "2023-11-22T12:10:30.000Z", date is stringified
console.log(clone.e); // undefined, function is lost
_.cloneDeep()
to deep clone an object. This allows you to handle more complex types and cases, such as circular references, prototype inheritance, and custom cloning logic.const _ = require('lodash');
const obj = { a: 1, b: { c: 2 }, d: new Date(), e: function () {} };
const clone = _.cloneDeep(obj); // deep clone
clone.a = 9; // change clone
console.log(obj.a); // 1, original is not affected
clone.b.c = 10; // change clone
console.log(obj.b.c); // 2, original is not affected
console.log(clone.d); // 2023-11-22T12:10:30.000Z, date is preserved
console.log(clone.e); // [Function: e], function is preserved
structuredClone()
method to deep clone a serializable object. This method isn’t a feature of the JavaScript language itself — instead it’s a feature of browsers and other JavaScript hosts that implement web APIs.// Define an object with circular references
const obj = { name: "Basit", age: 26 };
obj.self = obj;
// Serialize the object using structuredClone
const clone = structuredClone(obj);
// Check the clone
console.log(clone.name); // "Basit"
console.log(clone.age); // 26
console.log(clone.self === clone); // true
// Modify the clone
clone.name = "John";
clone.age = 30;
// Check the original object
console.log(obj.name); // "Basit", not affected
console.log(obj.age); // 26, not affected
console.log(obj.self === obj); // true, not affected
Cloning objects in programming, including JavaScript, is not always a trivial task due to the complexities inherent in the nature of objects and the way programming languages handle them. Here are some reasons why cloning objects can be challenging:
this
keyword or the arguments
object, which can affect the cloning process.Date
, RegExp
, Map
, Set
, or custom class instances, may not be cloned correctly by some methods, as they may lose some of their properties or methods.Shallow and deep cloning are two ways of copying objects in JavaScript. Shallow cloning means that only the first level of the object is copied, while deeper levels are referenced. Deep cloning means that all levels of the object are copied, creating a true copy of the original object. Here are some differences between shallow and deep cloning:
Object.assign()
method, or the spread operator. Deep cloning can be done using the JSON.parse()
and JSON.stringify()
methods, or using some libraries like lodash.cloneDeep()
or jQuery.extend()
.Cloning reference vs. value type in JavaScript is a topic that involves understanding how different types of data are stored, copied, and compared in the language.
A value type is a primitive data type, such as a number, string, boolean, null, or undefined. A value type is stored directly in a variable, and when it is copied to another variable, the value itself is copied. This means that changing one variable does not affect the other, and comparing two variables with the same value type will return true.
var x = 10; // x is a value type
var y = x; // y copies the value of x
x = 20; // changing x does not affect y
console.log (x); // 20
console.log (y); // 10
console.log (x == y); // false
A reference type is an object, such as an array, function, date, or user-defined object. A reference type is stored as a reference (or a pointer) to the location in memory where the object is stored. When it is copied to another variable, the reference is copied, not the object itself. This means that changing one variable will affect the other, and comparing two variables with the same reference type will return false, unless they point to the same object.
var x = [1, 2, 3]; // x is a reference type
var y = x; // y copies the reference of x
x[0] = 20; // changing x affects y
console.log (x); // [20, 2, 3]
console.log (y); // [20, 2, 3]
console.log (x == y); // true
var z = [20, 2, 3]; // z is a new reference type
console.log (x == z); // false
Cloning a value type is easy, as it can be done with a simple assignment. Cloning a reference type is more complicated, as it requires creating a new object and copying all the properties and values of the original object.