Javascript - 为什么对象数组不相等?

时间:2017-08-28 15:57:30

标签: javascript arrays object

我有一些javascript代码,我正在尝试清理...我最初用“暴力”风格编写了一个对象数组(称为myData)的定义。蛮力样式定义了4个对象的数组(数组总是大小为4)。以这种方式定义我的应用程序工作正常。程序后面的代码读取JSON并以类似于以下语法更新各种元素:

myData[2].Quarter = "Q2";

当我尝试清理/合并myData的定义时,我的代码运行时没有语法错误,但是数组的所有4个元素都以相同的值结束,其中在暴力样式中,数组中的每个对象最终有不同的价值观。唯一不同的是两个定义。 这就是我所说的“蛮力”,我的整个代码集工作得很好......它实际上是相同的对象被复制了4次。

var myData = [
     {
        Quarter: 'EMPTY',
        Field_Cloud:0,
        Field_Cloud_Renew:0,
        Field_On_Premise:0,
        Field_Total:0,
        OD_Cloud:0,
        OD_Cloud_Renew:0,
        OD_On_Premise:0,
        OD_Total:0,
        Field_OD_Total:0
    },
        {
        Quarter: 'EMPTY',
        Field_Cloud:0,
        Field_Cloud_Renew:0,
        Field_On_Premise:0,
        Field_Total:0,
        OD_Cloud:0,
        OD_Cloud_Renew:0,
        OD_On_Premise:0,
        OD_Total:0,
        Field_OD_Total:0
    },
        {
        Quarter: 'EMPTY',
        Field_Cloud:0,
        Field_Cloud_Renew:0,
        Field_On_Premise:0,
        Field_Total:0,
        OD_Cloud:0,
        OD_Cloud_Renew:0,
        OD_On_Premise:0,
        OD_Total:0,
        Field_OD_Total:0
    },
        {
        Quarter: 'EMPTY',
        Field_Cloud:0,
        Field_Cloud_Renew:0,
        Field_On_Premise:0,
        Field_Total:0,
        OD_Cloud:0,
        OD_Cloud_Renew:0,
        OD_On_Premise:0,
        OD_Total:0,
        Field_OD_Total:0
    }   
    ];

我尝试将其合并到我认为应该是相同的代码,但随后我的应用程序不再有效。症状是阵列的所有4个对象都以相同的值结束。

合并(但破损)的代码:

 var myDataStruct= {
        Quarter: 'EMPTY',
        Field_Cloud:0,
        Field_Cloud_Renew:0,
        Field_On_Premise:0,
        Field_Total:0,
        OD_Cloud:0,
        OD_Cloud_Renew:0,
        OD_On_Premise:0,
        OD_Total:0,
        Field_OD_Total:0
    };

    var myData = [];
    myData.push(myDataStruct);
    myData.push(myDataStruct);
    myData.push(myDataStruct);
    myData.push(myDataStruct);

我做错了什么,如何定义myData是一种合适的整合方式?

3 个答案:

答案 0 :(得分:6)

您可能希望使用面向对象的编程和继承:

var myDataStruct = {
    Quarter: 'EMPTY',
    Field_Cloud:0,
    Field_Cloud_Renew:0,
    Field_On_Premise:0,
    Field_Total:0,
    OD_Cloud:0,
    OD_Cloud_Renew:0,
    OD_On_Premise:0,
    OD_Total:0,
    Field_OD_Total:0
};

var myData = Array.from({length:4}, ()=>Object.create(myDataStruct));

或者你像这样克隆那个对象四次(不利于内存消耗,没有真正的优势):

var myData = Array.from({length:4}, ()=>Object.assign({},myDataStruct));

它们在记忆中的样子:

1)你的方法,将同一个obj推入数组:

//the object
123:
  {Quarter:"Empty",...}
//the array
124: 123
125: 123
126: 123
127: 123
128: null

2)继承方法:

//the prototype
123:
  {Quarter:"empty",...}
//the subobjects
124:
  { prototype: 123}
125:
  { prototype: 123}
126:
  { prototype: 123}
127:
  { prototype: 123}
//the array
128:124
129:125
130:126
131:127
132: null

因此,使用您的方法,更改其中一个数组元素,实际上会更改存储在123的对象,因此它们相互干扰。使用继承方法,例如更改第一个对象会在124更改对象,在propertylookup上,它将首先在124搜索属性,然后如果找不到它将在123中搜索。所以如果你这样做

myData[0].Quarter = "Full";

记忆将变为:

//the prototype
123:
  {Quarter:"empty",...}
//the subobjects
124:
  {Quarter: "Full", prototype: 123}

结果是:

myData[0].Quarter // "Full", looked up in 124
myData[1].Quarter // "empty", as not found in 125 and therefore looked up in 123

答案 1 :(得分:3)

问题是由于Javascript按引用处理对象

如果您创建一个对象并将其推送到某个阵列四次,则有四个引用到阵列中的同一个对象。如果之后更改了对象,则此更改似乎会影响数组中的所有对象。但这只是因为数组包含对一个对象的四个引用,而不是一个原始对象的四个副本。如果您需要添加对象的副本,则需要首先创建这些副本,这些副本在Javascript中不可用,与其他语言一样简单。

只需考虑存储对象的变量(包括Javascript中的函数和数组,每个对象都是更具体的对象),因为它包含存储对象的内存中的地址。如果你使用该变量来访问对象,它只是指向一些内存。如果将变量的值赋给某个​​其他变量,或者将其作为参数传递给某个函数,例如数组的push(),则复制并传递该地址,而不是对象的属性。

当涉及到复制/克隆对象时,需要遵守更多方面:如果您的对象具有对象(或数组或函数)类型的属性,您可能也想要复制这些对象。他们也可能依次拥有这些属性。那么,你需要一个副本的对象吗?对象可能具有您可以访问的属性,但某些操作不会对其进行处理,但您仍希望复制它们。此方面会影响可枚举与不可枚举的属性。最后,每个对象都可能拥有自己的属性,并且"派生"属性和一些操作处理所有这些属性或仅拥有对象的属性。

因此,请注意克隆对象实际需要的方法。

您可能会问:为什么Javascript不会隐式创建副本?我认为这是由于语言结构简单。一旦你处理了这个"问题"你可能会意识到Javascript的速度有多快。我宁愿自己照顾这个,而不是因为语言浪费时间来复制对象或实现写时复制魔术。

实施例

深层复制,慢速性能,所有可枚举属性

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

浅拷贝,良好性能,拥有可枚举属性

copy = Object.assign( {}, original );

more on objects

答案 2 :(得分:0)

您正在将相同的对象引用推送到数组中,这意味着当您从数组中修改一个对象时,所有这些对象都会被修改。

要解决此问题,您可以使用Object.assign()或Object Spread语法,例如myData.push(Object.assign({}, myDataStruct));,它将创建一个空对象,将其与您的myDataStruct对象合并,然后将其推入数组。

有用的链接: