【问题标题】:Modifying a copy of a JavaScript object is causing the original object to change修改 JavaScript 对象的副本会导致原始对象发生变化
【发布时间】:2022-01-07 11:18:06
【问题描述】:

我正在将objA 复制到objB

const objA = { prop: 1 }, 
const objB = objA; 
objB.prop = 2;
console.log(objA.prop); // logs 2 instead of 1

数组也有同样的问题

const arrA = [1, 2, 3], 
const arrB = arrA; 
arrB.push(4); 
console.log(arrA.length); // `arrA` has 4 elements instead of 3.

【问题讨论】:

标签: javascript object copy


【解决方案1】:

尝试使用$.extend()

但是,如果您想保留两个原始对象,您可以 可以通过传递一个空对象作为目标来做到这一点:

var object = $.extend({}, object1, object2);


var tempMyObj = $.extend({}, myObj);

【讨论】:

  • 宾果游戏,这就是我要写的...... :)
  • JavaScript 现在有一个类似的内置函数,Object.assign()。用法示例:Object.assign({}, myObj).
【解决方案2】:

很明显,您对声明 var tempMyObj = myObj; 的作用有一些误解。

在 JavaScript 中,对象是通过引用传递和分配的(更准确地说是引用的值),所以 tempMyObjmyObj 都是对同一个对象的引用。

这是一个简化的插图,可以帮助您直观地了解正在发生的事情

// [Object1]<--------- myObj

var tempMyObj = myObj;

// [Object1]<--------- myObj
//         ^ 
//         |
//         ----------- tempMyObj

正如你在赋值后看到的,两个引用都指向同一个对象。

如果您需要修改一个而不是另一个,则需要创建一个副本

// [Object1]<--------- myObj

const tempMyObj = Object.assign({}, myObj);

// [Object1]<--------- myObj
// [Object2]<--------- tempMyObj

旧答案:

以下是创建对象副本的其他几种方法

既然你已经在使用 jQuery:

var newObject = jQuery.extend(true, {}, myObj);

使用原生 JavaScript

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var newObject = clone(myObj);

herehere

【讨论】:

  • @JAAulde,感谢您更新答案,发布您对问题的更新。我不确定我是否会抓住它。
  • 没问题!当我对已回答的问题进行如此广泛的编辑时,我会尝试跟进。 :)
  • 可能值得注意的是,当您创建自己的对象时,这似乎开始抬头(例如 let a={}; let b=a; b.blah=1;console.log(a) )。我猜很多新手(像我一样)不会注意到这一点,因为许多命令会为我们输出一个新对象(例如 map/filter/etc)。我很难理解为什么有人会理智地想要当前的行为......
  • Object.assign({}, myObj); 没有为我创建副本。取而代之的是 var tempMyObj = Object.create(myObj); 工作。
  • 此答案还应包括如何使用数组执行此操作:const arrB = arrA.slice();const arrB = Array.from(arrA);const arrB = [ ...arrA ];
【解决方案3】:

尝试使用 create() 方法,如下所述。

var tempMyObj = Object.create(myObj);

这将解决问题。

【讨论】:

  • 虽然你的答案是正确的,而且绝对适合这里。但是使用 Object.create() 有一个缺点。它将属性添加到对象的 dunder proto _proto_ 而不是对象。 Object.assign() 将属性添加到对象。
【解决方案4】:

这可能很棘手,让我试着用一种简单的方式来表达。当您在 javascript 中将一个变量“复制”到另一个变量时,实际上并没有将其值从一个变量复制到另一个变量,而是将 对原始对象的引用分配给复制的变量。要实际制作副本,您需要创建一个新对象使用

棘手的部分是因为为复制的变量分配新值和修改其值之间存在差异。当您为复制变量分配新值时,您正在摆脱引用并将新值分配给副本,但是,如果您只修改副本的值(不分配新值),您正在修改副本和原始.

希望示例有所帮助!

let original = "Apple";
let copy1 = copy2 = original;
copy1 = "Banana";
copy2 = "John";

console.log("ASSIGNING a new value to a copied variable only changes the copy. The ogirinal variable doesn't change");
console.log(original); // Apple
console.log(copy1); // Banana
console.log(copy2); // John 

//----------------------------

original = { "fruit" : "Apple" };
copy1 = copy2 = original;
copy1 = {"animal" : "Dog"};
copy2 = "John";

console.log("\n ASSIGNING a new value to a copied variable only changes the copy. The ogirinal variable doesn't change");
console.log(original); //{ fruit: 'Apple' }
console.log(copy1); // { animal: 'Dog' }
console.log(copy2); // John */

//----------------------------
// HERE'S THE TRICK!!!!!!!

original = { "fruit" : "Apple" };
let real_copy = {};
Object.assign(real_copy, original);
copy1 = copy2 = original;
copy1["fruit"] = "Banana"; // we're not assiging a new value to the variable, we're only MODIFYING it, so it changes the copy and the original!!!!
copy2 = "John";


console.log("\n MODIFY the variable without assigning a new value to it, also changes the original variable")
console.log(original); //{ fruit: 'Banana' } <====== Ops!!!!!!
console.log(copy1); // { fruit: 'Banana' }
console.log(copy2); // John 
console.log(real_copy); // { fruit: 'Apple' } <======== real copy!

【讨论】:

    【解决方案5】:

    使用 JSON.parse() 和 JSON.stringify 深度克隆对象

    // Deep Clone
    obj = { a: 0 , b: { c: 0}};
    let deepClone = JSON.parse(JSON.stringify(obj));
    

    参考:this article

    更好的参考this article

    【讨论】:

      【解决方案6】:

      如果你对数组也有同样的问题,那么这里是解决方案

      let sectionlist = [{"name":"xyz"},{"name":"abc"}];
      let mainsectionlist = [];
      for (let i = 0; i < sectionlist.length; i++) {
           mainsectionlist[i] = Object.assign({}, sectionlist[i]);
      }
      

      【讨论】:

        【解决方案7】:

        用三个点在新变量中展开对象

        const a = {b: 1, c: 0};
        let d = {...a};
        

        【讨论】:

        • 云你开发一点你的答案来解释为什么传播算子也在工作?它可以帮助其他人理解你的答案
        【解决方案8】:

        将原始对象序列化为 JSON 并反序列化为另一个相同类型的对象变量。这将为您提供具有所有属性值的对象副本。并且对原始对象的任何修改都不会影响复制的对象。

        string s = Serialize(object); //Serialize to JSON
        //Deserialize to original object type
        tempSearchRequest = JsonConvert.DeserializeObject<OriginalObjectType>(s);
        

        【讨论】:

        • 这是一个与 JavaScript 如何处理引用有关的 JavaScript 特定问题,为什么您的代码示例是 .NET?
        【解决方案9】:

        总而言之,为了澄清,复制 JS 对象有三种方式。

        1. 普通副本。当您更改原始对象的属性时,复制的对象的属性也会更改(反之亦然)。
        const a = { x: 0}
        const b = a;
        b.x = 1; // also updates a.x
        
        1. 浅拷贝。对于原始对象和复制对象,顶级属性将是唯一的。不过,嵌套属性将在两个对象之间共享。使用扩展运算符 ...{}Object.assign()
        const a = { x: 0, y: { z: 0 } };
        const b = {...a}; // or const b = Object.assign({}, a);
        
        b.x = 1; // doesn't update a.x
        b.y.z = 1; // also updates a.y.z
        
        1. 深拷贝。所有属性对于原始对象和副本对象都是唯一的,甚至是嵌套属性。对于深拷贝,将对象序列化为 JSON 并将其解析回 JS 对象。
        const a = { x: 0, y: { z: 0 } };
        const b = JSON.parse(JSON.stringify(a)); 
        
        b.y.z = 1; // doesn't update a.y.z
        
        1. 使用Object.create() 确实会创建一个新对象。属性在对象之间共享(更改一个也会更改另一个)。与普通副本的不同之处在于,属性是在新对象的原型__proto__ 下添加的。当您从不更改原始对象时,这也可以作为浅拷贝,但我建议使用上述方法之一,除非您特别需要这种行为。

        【讨论】:

          猜你喜欢
          • 2018-10-21
          • 2021-10-19
          • 2020-04-24
          • 1970-01-01
          • 2020-12-30
          相关资源
          最近更新 更多