【问题标题】:Object.assign() and Spread properties still mutating originalObject.assign() 和 Spread 属性仍在改变原始属性
【发布时间】:2018-04-24 21:07:09
【问题描述】:

我正在尝试将数组元素的值分配给对象。在第一次尝试类似bar = foo[0]; 之后,我发现对bar 的任何更改也会更改foo[0],因为具有相同的引用。

太棒了,没人想到,在阅读了 immutability 和 ES6 Object.assign() 方法和 spread 属性后,我认为它会解决这个问题。但是,在这种情况下它不会。我错过了什么?

编辑:抱歉,accountTypes 混淆了,我修正了这个例子。 另外,我想保留 Settings 的类结构,所以 let copy = JSON.parse(JSON.stringify(original)); 在这种情况下并不是我真正想要的。

//this object will change according to a selection
currentPreset;

//this should remain unchanged
presets: {name: string, settings: Settings}[] = [];


  ngOnInit()
  {
    this.currentPreset = {
      name: '',
      settings: new Settings()
    }

    this.presets.push({name: 'Preset1', settings: new Settings({
        settingOne: 'foo',
        settingTwo: false,
        settingThree: 14
      })
    });

  }

 /**
  * Select an item from the `presets` array and assign it, 
  * by value(not reference), to `currentPreset`.
  * 
  * @Usage In an HTML form, a <select> element's `change` event calls 
  * this method to fill the form's controls with the values of a
  * selected item from the `presets` array. Subsequent calls to this
  * method should not affect the value of the `presets` array.
  *
  * @param value - Expects a numerical index or the string 'new'
  */
  setPreset(value)
  {
    if(value == 'new')
    {
      this.currentPreset.name = '';
      this.currentPreset.settings.reset();
    }
    else
    {
      this.currentPreset = {...this.presets[value]};
      //same as above
      //this.currentPreset = Object.assign({}, this.presets[value]);
    }
  }

【问题讨论】:

  • 你能提供一个更完整或更好的例子吗? accountTypes 是什么? value 是什么?什么是您不想被突变的值,突变之前是什么,突变之后是什么?
  • 使用这些操作符,您正在制作Settings 的卷影副本,在所有更新中都是相同的实例
  • 使用对象扩展或Object.assign 只会创建一个浅拷贝。因此,该副本的变异属性仍将更改原始属性中的相同属性。您可能想尝试创建深层副本。 this question 的答案显示了一些这样做的方法。

标签: javascript typescript ecmascript-6


【解决方案1】:

试试这个:let copy = original.map(item =&gt; Object.assign({}, ...item)); 这将创建一个新对象,而不引用旧对象original

如果您想对数组执行此操作,请尝试使用 []

let copy = original.map(item => Object.assign([], ...item));

【讨论】:

    【解决方案2】:

    你必须做一个深拷贝,这是最简单的方法:

    let copy = JSON.parse(JSON.stringify(original));
    

    【讨论】:

    • 小心,因为 JSON 只存储普通对象,而不是具有自定义原型的特定类型的对象。我不能老实说这对 OP 的使用是否重要,因为它们并没有真正显示足够的代码来知道,但是如果有 Settings 对象或任何其他类型的自定义对象,那么这种类型的深拷贝将不会保留自定义对象类型/原型。
    • @Luis,感谢您的回答。我忘了提及它,但我确实想保留 Settings 对象,正如 jfriend00 指出的那样。
    【解决方案3】:

    这并不能真正回答问题,但由于我试图不改变的对象内部没有嵌套属性,所以我在属性级别调用赋值,这里的浅拷贝很好。

    setPreset(value)
      {
        if(value == 'new')
        {
          this.currentPreset.name = '';
          this.currentPreset.settings.reset();
        }
        else
        {
          this.currentPreset.name = this.presets[value].name;
          this.currentPreset.privileges = Object.assign(new Settings(), 
                                             this.presets[value].settings);
        }
      }
    

    一个更好的解决方案,因为无论如何我都在创建一个new Settings(),可能是将这个逻辑移动到一个Settings 类方法并在构造函数中调用它

    【讨论】:

      【解决方案4】:

      我最近遇到了同样的问题,我不明白为什么我的一些对象会改变它们的属性。我不得不更改我的代码以避免突变。这里的一些答案帮助我后来理解了,比如这篇很棒的文章:https://alistapart.com/article/why-mutation-can-be-scary/

      我推荐它。作者提供了很多示例和有用的库,在嵌入式属性方面可以胜过Object.assign()

      【讨论】:

        猜你喜欢
        • 2018-08-29
        • 2020-07-13
        • 2017-05-26
        • 2016-07-06
        • 2020-03-28
        • 2017-07-30
        • 1970-01-01
        • 1970-01-01
        • 2021-07-11
        相关资源
        最近更新 更多