【问题标题】:Javascript Object Assignment Infinite recursionJavascript对象赋值无限递归
【发布时间】:2015-09-12 17:31:59
【问题描述】:

我有一个我正在努力解决的问题。任何帮助将不胜感激。

我有一个对象,我将当前对象状态分配给当前对象的一个​​属性。

下面的例子:

var product = {
  ropeType: 'blah',
  ropePrice: 'blah',
  ropeSections: {
    name: 'blaah',
    price: 'blaah'
  },
  memory: false
}

product.memory = product;

现在,当我在控制台中查看产品对象时,我得到了 Product.memory.Product.memory.Product.... 的无限递归。

截图如下:

我知道它与对象引用自身有关,但我似乎无法理解这个概念。谁能解释一下?

我尝试这样做的原因是为了在本地存储中保存对象的当前状态。

我希望我说得通。

【问题讨论】:

  • 你不明白的到底是什么?这是一个循环数据结构。考虑一个圆形路径。如果沿着这条路走,你最终会到达你开始的地方。
  • 使用不同的变量来存储对象。如果你将一个对象附加到它自身的一个属性上,它就会无限地自我引用。
  • 哦,好吧,所以对象总是自引用的?
  • 嗯,不。但是对象是通过引用来表示的。 product.memory = product; 将对 product 的引用分配给自身。

标签: javascript oop object recursion


【解决方案1】:

我将当前对象状态分配给当前对象的一个​​属性。

不,您创建了一个引用自身的属性。

如果要保存属性的当前状态,则需要克隆对象。

如果你想创建一个对象的(浅)副本,那么你可以使用:

function clone(obj) {
    if(obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

    var temp = obj.constructor();

    for(var key in obj) {
        if(Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = obj[key];
            delete obj['isActiveClone'];
        }
    }    

    return temp;
}

[code taken from here - 并稍作修改以进行浅拷贝而不是递归深拷贝]

然后做:

product.memory = clone( product );

如果您第二次克隆它,您可能会发现递归问题,它会复制 product.memory 以及对象的其余部分。在这种情况下,只需delete product.memory,然后再进行后续克隆。

类似:

function saveCurrentState( obj ){
  if ( 'memory' in obj )
    delete obj.memory;
   obj.memory = clone( obj );
}

一边

如果你想要一个深拷贝,那么你可以这样做:

function deepCopy(obj){
  return JSON.parse(JSON.stringify(obj));
}

[As suggested here - 但请注意它对 Date 对象的注意事项]

【讨论】:

【解决方案2】:

您可以通过将当前产品克隆为新产品来实现您的想法。我们有Object.keys 来获取对象的所有属性。所以这是我的想法:

product = {
   ropeType: 'blah',
   ropePrice: 'blah',
   ropeSections: {
      name: 'blaah',
      price: 'blaah'
   },
   memory: false
};
var keys = Object.keys(product);
var newProduct = {};
keys.forEach(function(key){
   if(key === 'memory') return;
   newProduct[key] = product[key];
});
product.memory = newProduct;

【讨论】:

  • Object.keys() 并非所有浏览器都支持 - 即 IE8 和更早版本以及其他旧版本的浏览器。
  • 他只要求 ES5,所以我认为它会很好用 :)。
【解决方案3】:

您可能想要转换该对象的状态,而不是实际存储对对象的引用。也许通过将其克隆到一个新对象上,或者可能将其保留为 JSON 字符串(如果您使用的是 localStorage,您会想要这样做)。

由于您可能希望在检查memory 属性时查看对象的当前 状态,因此您应该将memory 设为执行该转换的函数。

可能是这样的:

var product = {
  ropeType: 'blah',
  ropePrice: 'blah',
  ropeSections: {
    name: 'blaah',
    price: 'blaah'
  },
  memory: function() {     
    return JSON.stringify(this);
  }
}

然后您可以调用 product.memory() 并以 JSON 格式获取其状态。

【讨论】:

    【解决方案4】:

    这就是问题所在:

    product.memory = product;

    您正在将一个对象的引用分配给它自己。 JavaScript 通过引用传递对象,因此它永远不会通过赋值存储自身的克隆。

    如果您希望记录一段时间内对对象所做的修改,最好的方法是使用数组来保存它的克隆副本(或至少保存已更改的属性)。

    举个最快的例子:

    var Product    =    function(){
    
    };
    
    var product       =    new Product();
    product.history   = [];
    product.saveState = function(){
        var changes = {};
    
        for(var i in this){
    
            /** Prevent infinite self-referencing, and don't store this function itself. */
            if(this[i] !== this.history && this[i] !== this.saveState){
                changes[i] = this[i];
            }
        }
    
        this.history.push(changes);
    };
    

    显然,在 JavaScript 中有很多更好的方法可以实现这一点,但它们需要更多解释。基本上,循环访问一个对象来存储它的属性不可避免地会碰到它们被分配到的属性,所以在某些时候需要检查以防止自引用。

    【讨论】:

    • 好的,谢谢,您将如何实现我想要做的事情?
    • 完成。请记住,这不是最好的解决方案,但如果您还不熟悉 JavaScript 中的属性分配和引用的工作原理,它可能是最容易解释的。
    猜你喜欢
    • 2013-05-12
    • 2012-03-14
    • 1970-01-01
    • 1970-01-01
    • 2018-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-06
    相关资源
    最近更新 更多