我有一个无休止循环对我的一些库代码。
我不是如何在JavaScript中最好的实施周期检测和避免清晰。 即有一个对象是否从“此”参考来检查的无编程方式,不是吗?
下面的代码。 谢谢!
setAttrs: function(config) {
var go = Kinetic.GlobalObject;
var that = this;
// set properties from config
if(config !== undefined) {
function setAttrs(obj, c) {
for(var key in c) {
var val = c[key];
/*
* if property is an object, then add an empty object
* to the node and then traverse
*/
if(go._isObject(val) && !go._isArray(val) && !go._isElement(val)) {
if(obj[key] === undefined) {
obj[key] = {};
}
setAttrs(obj[key], val); // <--- offending code;
// one of my "val"s is a "this" reference
// to an enclosing object
}
我知道对付这种情况的“可靠,干净”的方法是使用“访问”对象的集合,然后反应过来 - 终止,插入符号引用,等等 - 基于如果当前对象已被“参观“ 或不。
克罗克福德先生将使用这种方法cycle.js和他使用该集合的数组。 摘抄:
// If the value is an object or array, look to see if we have already
// encountered it. If so, return a $ref/path object. This is a hard way,
// linear search that will get slower as the number of unique objects grows.
for (i = 0; i < objects.length; i += 1) {
if (objects[i] === value) {
return {$ref: paths[i]};
}
}
这是不幸的是没有可能的,因为它缺乏一个身份映射到用于这个原始的“散列”的方法在JavaScript中。 同时阵列收集范围是O(n^2)
这并不像听起来那么糟糕 :
这是因为,如果“访问”收藏只是一个后卫那么价值n
是堆叠的只是深度:唯一周期是重要的,而复制同一个对象多次是没有的。 也就是说,在“访问”收集的对象可以在堆栈退绕修剪。
在cycle.js代码的“访问”收集不能被修剪,因为它必须保证同一符号名称给定对象总是用来允许恢复时,它的序列化“保持引用”。 然而,即使在这种情况下, n
仅遍历独特的非原始值的数量。
我能想到的唯一的其他方法将需要直接添加一个“访问属性”的对象被走过,我会考虑一般不希望的特征。 (然而,见BERGI的关于这件神器是[比较]易清理评论。)
编码愉快。
OK,我有兴趣怎么说“参观” @pst提到可能看起来像财产,所以我这个编码:
Object.copyCircular = function deepCircularCopy(o) {
const gdcc = "__getDeepCircularCopy__";
if (o !== Object(o))
return o; // primitive value
var set = gdcc in o,
cache = o[gdcc],
result;
if (set && typeof cache == "function")
return cache();
// else
o[gdcc] = function() { return result; }; // overwrite
if (o instanceof Array) {
result = [];
for (var i=0; i<o.length; i++) {
result[i] = deepCircularCopy(o[i]);
} else {
result = {};
for (var prop in o)
if (prop != gdcc)
result[prop] = deepCircularCopy(o[prop]);
else if (set)
result[prop] = deepCircularCopy(cache);
}
if (set)
o[gdcc] = cache; // reset
else
delete o[gdcc]; // unset again
return result;
};
请注意,这只是一个例子。 它不支持:
- 非纯对象。 与原型(除了阵列)一切都不会被复制,但复制到一个
new Object
! 这包括功能! - 跨全球范围的对象:它使用
instanceof Array
。 - 属性描述符等设置器/吸气剂,不可写入和nonenumerable属性
超值服务:
- 它不使用一个大的阵列,其它需要在每次遇到一个对象时间搜索。
缺点:
- 其上有一个对象不起作用
__getDeepCircularCopy__
并不它所声称该方法。 虽然方法(与函数作为值属性)在此轻量级版本反正支持。
该解决方案将工作与循环引用,复制圆形结构的物体, 没有一个无限循环结束 。 请注意,“通知”将在这里意味着一个属性,在“树”引用其“父母”之一:
[Object]_ [Object]_
/ |\ / |\
prop | prop |
\_____/ | |
\|/ |
[Object] |
\ |
prop |
\___/
树木共享叶子会不会被复制的结构,他们将成为两个独立的leafes:
[Object] [Object]
/ \ / \
/ \ / \
|/_ _\| |/_ _\|
[Object] [Object] ===> [Object] [Object]
\ / | |
\ / | |
_\| |/_ \|/ \|/
[Object] [Object] [Object]
除非你要跟踪复制每个属性的。
但如果你是确保每一个属性是null
,字符串,数字数组或一个简单的对象,你可以赶上JSON.stringify
例外,看看是否有反向引用,就像这样:
try {
JSON.stringify(obj);
// It's ok to make a deep copy of obj
} catch (e) {
// obj has back references and a deep copy would generate an infinite loop
// Or finite, i.e. until the stack space is full.
}
这只是一个想法,我没有的表演的想法。 我担心这可能对大的物体很慢。
这里有一个简单的递归clone方法。 和许多人一样的解决方案,大多数非基本属性将分享与原来的对象(如函数)的引用。
它通过保持一个地图中引用的对象,以便后续引用可以共享相同的克隆处理无限循环。
const georgeCloney = (originalObject, __references__ = new Map()) => {
if(typeof originalObject !== "object" || originalObject === null) {
return originalObject;
}
// If an object has already been cloned then return a
// reference to that clone to avoid an infinite loop
if(__references__.has(originalObject) === true) {
return __references__.get(originalObject);
}
let clonedObject = originalObject instanceof Array ? [] : {};
__references__.set(originalObject, clonedObject);
for(let key in originalObject) {
if(originalObject.hasOwnProperty(key) === false) {
continue;
}
clonedObject[key] = georgeCloney(originalObject[key], __references__);
}
return clonedObject;
};
用法示例...
let foo = {};
foo.foo = foo;
let bar = georgeCloney(foo);
bar.bar = "Jello World!";
// foo output
// {
// foo: {
// foo: {...}
// }
// }
//
// bar output
// {
// foo: {
// foo: {...},
// bar: "Jello World!"
// },
// bar: "Jello World!"
// }
文章来源: In Javascript, when performing a deep copy, how do I avoid a cycle, due to a property being “this”?