带有循环引用的Javascript深度克隆对象 [英] Javascript Deep Clone Object with Circular References
问题描述
我已经从Dmitriy Pichugin的现有答案复制了以下功能。这个函数可以深度克隆一个没有任何循环引用的对象 - 它可以工作。
I have copied the function below from an existing answer by Dmitriy Pichugin. This function can deep clone an object without any circular references- it works.
function deepClone( obj ) {
if( !obj || true == obj ) //this also handles boolean as true and false
return obj;
var objType = typeof( obj );
if( "number" == objType || "string" == objType ) // add your immutables here
return obj;
var result = Array.isArray( obj ) ? [] : !obj.constructor ? {} : new obj.constructor();
if( obj instanceof Map )
for( var key of obj.keys() )
result.set( key, deepClone( obj.get( key ) ) );
for( var key in obj )
if( obj.hasOwnProperty( key ) )
result[key] = deepClone( obj[ key ] );
return result;
}
然而,我的程序无限循环,我意识到这是由于循环引用。
However, my program loops indefinitely and I have realised that this is due to a circular reference.
循环引用的示例:
function A() {}
function B() {}
var a = new A();
var b = new B();
a.b = b;
b.a = a;
推荐答案
我建议使用Map来映射对象他们的副本在目的地的来源。事实上,我最终使用的是 WeakMap
,如Bergi所建议的那样。每当源对象在地图中时,都会返回其相应的副本而不是进一步递归。
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.
同时原始 deepClone
代码可以进一步优化:
At the same time some of the code in the original deepClone
code can be optimised further:
-
原始值的第一部分测试有一个小的问题:它将
新的Number(1)
与new Number(2)
区别对待。这是因为第一个中的
。它应该更改为==
,如果===
。但实际上,前几行代码似乎等同于此测试:Object(obj)!== obj
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
这需要ES6支持:
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
这篇关于带有循环引用的Javascript深度克隆对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!