【问题标题】:Manipulating object in array changes object outside of array?操作数组中的对象会更改数组外的对象吗?
【发布时间】:2017-12-04 19:00:56
【问题描述】:

我不明白为什么会发生这种行为。假设我定义了一个对象并制作了一个包含 3 个该对象的数组。如果我修改数组中的对象,它会影响对象的所有实例吗?有人可以解释这是为什么吗?另外,如何使用对象的独立“副本”创建一个数组以获得所需的行为?谢谢!

例子

testObject = {"value1":"a","value2":"b"};
objArray = [];
for(i=0; i < 3; i++){
   var newobj = testObject; //make a new testObject
   objArray.push(newobj); //push new object to array
   }
delete objArray[0].value2 // Desired, delete value 2 ONLY from array object 0
objArray[2].value2 //Undefined? Why is value2 missing from object 2 
testObject.value2 //Undefined? Why is value2 missing from original object?

【问题讨论】:

  • "var newobj = testObject; //make a new testObject":评论有误。这不会创建新对象,而是共享现有对象。
  • 顺便说一句,value1value2 只是占位符名称,还是您实际上打算使用像数组一样的对象?

标签: javascript arrays node.js


【解决方案1】:

与原语(字符串、数字、布尔值、符号 null、未定义)相反,javascript 中的对象由reference 传递。变量充当这些对象的占位符/指针。要创建一个没有突变风险的对象副本,您可以使用spread(除非兼容):

const newObject = { ...testObject };

或者传统上,Object.assign(),传递一个空对象字面量以避免原始testObject的可变性:

const newObject = Object.assign({}, testObject);

深度克隆而言,MDN 建议使用JSON.parse()JSON.stringify() 的组合。比如:

const testObject = { value: "a", other: { value2: b } }; 

const newObject = JSON.parse(JSON.stringify(testObject));

【讨论】:

  • 好收获。固定。
  • 这几乎可以工作,但如果对象中有对象,也会出现同样的问题。我如何使它们也独立?我必须分层对象分配吗?示例:testObject = {"value:"a", "Other":{"value2":"b"}}; newObject = Object.assign({}, testObject); 删除 newObject.Other.value2 testObject.Other.value2 //未定义
  • JS中没有引用传递。您正在按值传递对对象的引用。
【解决方案2】:

您在循环中一次又一次地推送同一个对象的引用。

for(i=0; i < 3; i++){
   var newobj = testObject; //no new object,same object's reference again 
   objArray.push(newobj); //push new object to array
   }

应该是

for(i=0; i < 3; i++){
   var newobj =  {"value1":"a","value2":"b"}; //make a new testObject
   objArray.push(newobj); //push new object to array
   }

【讨论】:

    【解决方案3】:

    在 JavaScript 中创建对象时,实际上是在为该对象创建 引用。您可以将它存储在一个变量中并传递它......也许将它附加到一个数组中。当您对对象引用进行操作时,它会找到引用指向的原始对象并对其进行更新。因此,当您使用.push 时,它不会创建一个新对象,而只是推送对该对象的引用。如果您在一个地方更新它,它会在另一个地方以及您分配了该引用的任何其他地方更新它。

    将一个对象复制到一个新对象中通常称为克隆。在 JavaScript 中克隆对象有很多不同的方法,结果各不相同。

    您可以按照其他答案的建议使用var newobj = { ...testObject }。这个扩展运算符本质上复制了testObject 的属性并创建了一个新对象(用外部{} 声明)。然后将对该new 对象的引用分配给newobj。你可以认为它是这样做的:

    var newobj = {
      value1: testObject.value1,
      value2: testObject.value2,
    };
    

    然而,您应该记住,这只给您一个级别的克隆。也就是说,如果您的对象包含其他对象,那么对该对象的 reference 将被分配为属性,而不是该对象的克隆。例如:假设你有:

    var testObject = { obj: { a: "b" } };
    var newobj = { ...testObject };
    delete testObject.obj.a;
    console.log(newobj); // { obj: {} }
    

    为了解决这个问题,您需要执行所谓的深度克隆,这在 JavaScript 中可以通过递归克隆对象属性来完成,这些属性也是对象。有很多方法可以做到这一点,包括像 lodash 这样的库或自制函数。关于 SO 的一个例子:What is the most efficient way to deep clone an object in JavaScript?

    最后,如果testObject 应该是类似对象模板或其他newobj 派生自的初始状态,则使用函数可能更有意义:

    function newObjFactory() {
      return {
        value1: "a",
        value2: "b",
      };
    }
    

    然后你可以做var newobj = newObjFactory(),你每次都会得到一个新对象,因为每次调用函数和returned都会创建一个新对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-06
      • 1970-01-01
      相关资源
      最近更新 更多