How to Deep Clone an Object in Javascript/NodeJs
There are three ways to clone an object/variable in Javascript/NodeJs: Recursive, JSON, and Message Channel.
Deep Clone Object using Recursive Algorithm
To Deep Copy a variable in Javascript, we have to first check its type – if it is an Array, we then need to clone each element in the array as a new copy and if it is an Object, we have to iterate the keys and assign a cloned copy (deep copy) to the new object. It is primitive type e.g. int, float, we can just return a value – copy by value.
function clone(Obj) {
let buf; // the cloned object
if (Obj instanceof Array) {
buf = []; // create an empty array
var i = Obj.length;
while (i --) {
buf[i] = clone(Obj[i]); // recursively clone the elements
}
return buf;
} else if (Obj instanceof Object) {
buf = {}; // create an empty object
for (const k in Obj) {
if (obj.hasOwnProperty(k)) { // filter out another array's index
buf[k] = clone(Obj[k]); // recursively clone the value
}
}
return buf;
} else {
return Obj;
}
}
Example usage:
var a = [1, 2, "abc", { "a" : [b, 3, 4] }, true];
var b = clone(a);
It should be noted that when cloning the elements of array, and the values in an Object, we have to recursively call the function to clone the value – as the element could be another array or Object, or any other types e.g. booleans – who knows how weird the Javascript can get.
However, this may not work for objects with recursive references, for example:
let obj = { a: 1 };
obj.b = obj
let obj2 = clone(obj);
VM657:13 Uncaught RangeError: Maximum call stack size exceeded
at Object.hasOwnProperty (<anonymous>)
at clone (<anonymous>:13:15)
at clone (<anonymous>:14:18)
at clone (<anonymous>:14:18)
at clone (<anonymous>:14:18)
at clone (<anonymous>:14:18)
at clone (<anonymous>:14:18)
at clone (<anonymous>:14:18)
at clone (<anonymous>:14:18)
at clone (<anonymous>:14:18)
We can fix this by using a Hash Map to remember the fields that we have cloned. Here is a correct version that clones an object even with recursive references:
function deepClone(obj) {
const objectMap = new Map();
const _deepClone = (value) => {
const type = typeof value;
if (type !== "object" || type === null) {
return value;
}
if (objectMap.has(value)) {
return objectMap.get(value);
}
const result = Array.isArray(value) ? [] : {};
objectMap.set(value, result);
for (const key in value) {
result[key] = _deepClone(value[key]);
}
return result;
};
return _deepClone(obj);
}
Deep Clone Object using JSON parse/stringify
We can use the JSON parse/encode function to convert object to a string and then decode into the clone object.
function clone(obj) {
return JSON.parse(JSON.stringify(obj));
}
However, this method does not support cloning the objects with recursive referecnes as well.
let obj = { a: 1 };
obj.b = obj
let obj2 = clone(obj);
VM1129:2 Uncaught TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
--- property 'b' closes the circle
at JSON.stringify ()
at clone ( :2:27)
at :3:12
Deep Clone Object using Message Channel
We can create a message channel via MessageChannel then we can create two ports which can communicate to each other. This is usually used with the Web-Worker.
So, we can send the object from one port to another. And then we can receive the object (as a clone) on the other port. We wrap this using Promise (Async Programming).
function deepClone(obj) {
return new Promise((resolve) => {
const { port1, port2 } = new MessageChannel();
port1.postMessage(obj);
port2.onmessage = (msg) => {
resolve(msg.data);
}
}
}
This method supports cloning objects with self-references.
–EOF (The Ultimate Computing & Technology Blog) —
766 wordsLast Post: How to restore SQL database backup by using SQL Server Management Studio?
Next Post: C# How to Remove Empty Elements from List in O(n)?
