在Javascript中,进行了深刻的副本时,如何避免一个周期,由于产权是“本”?(In Javas

2019-06-24 18:00发布

我有一个无休止循环对我的一些库代码。

我不是如何在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
                }

Answer 1:

我知道对付这种情况的“可靠,干净”的方法是使用“访问”对象的集合,然后反应过来 - 终止,插入符号引用,等等 - 基于如果当前对象已被“参观“ 或不。

克罗克福德先生将使用这种方法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的关于这件神器是[比较]易清理评论。)

编码愉快。



Answer 2:

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]


Answer 3:

除非你要跟踪复制每个属性的。

但如果你是确保每一个属性是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.
}

这只是一个想法,我没有的表演的想法。 我担心这可能对大的物体很慢。



Answer 4:

这里有一个简单的递归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”?