Javascript Deep Clone Object with Circular References
There is now structuredClone in the Web API which also works with circular references.
Previous answer
I would suggest to use a Map to map objects in the source with their copy in the destination. In fact, I ended up using WeakMap
as suggested by Bergi. Whenever a source object is in the map, its corresponding copy is returned instead of recursing further.
At the same time some of the code in the original deepClone
code can be optimised further:
The first part testing for primitive values has a small issue: it treats
new Number(1)
differently fromnew Number(2)
. This is because of the==
in the firstif
. It should be changed to===
. But really, the first few lines of code seem then equivalent to this test:Object(obj) !== obj
I also rewrote some
for
loops into more functional expressions
This needs ES6 support:
function deepClone(obj, hash = new WeakMap()) {
// Do not try to clone primitives or functions
if (Object(obj) !== obj || obj instanceof Function) return obj;
if (hash.has(obj)) return hash.get(obj); // Cyclic reference
try { // Try to run constructor (without arguments, as we don't know them)
var result = new obj.constructor();
} catch(e) { // Constructor failed, create object without running the constructor
result = Object.create(Object.getPrototypeOf(obj));
}
// Optional: support for some standard constructors (extend as desired)
if (obj instanceof Map)
Array.from(obj, ([key, val]) => result.set(deepClone(key, hash),
deepClone(val, hash)) );
else if (obj instanceof Set)
Array.from(obj, (key) => result.add(deepClone(key, hash)) );
// Register in hash
hash.set(obj, result);
// Clone and assign enumerable own properties recursively
return Object.assign(result, ...Object.keys(obj).map (
key => ({ [key]: deepClone(obj[key], hash) }) ));
}
// Sample data
function A() {}
function B() {}
var a = new A();
var b = new B();
a.b = b;
b.a = a;
// Test it
var c = deepClone(a);
console.log('a' in c.b.a.b); // true
Since object cloning has a lot of pitfalls (circular references, proto chains, Set/Map, etc)
I suggest you to use one of well-tested popular solutions.
Like, lodash's _.cloneDeep or 'clone' npm module.