简体   繁体   English

避免更改JavaScript数组

[英]Avoid Mutating JavaScript array

Below is my code 下面是我的代码

let traces = { ref: null, min: null, max: null, avg: null };
let learning = {
    "Application": "b3",
    "t": [
        {
            "d": 2,
            "BinaryType": "Current"
        },
        {
            "d": 3,
            "BinaryType": "Max"
        },
        {
            "d": 4,
            "BinaryType": "Avg"
        },
        {
            "d": 5,
            "BinaryType": "Min"
        }
    ]
};

let traceArr = Object.assign([],learning.t);

traceArr.forEach(trace => {

    if (trace.BinaryType == 'Current') {            
        traces.ref = Object.assign({}, learning);   
        traces.ref.t = [];
        traces.ref.t.push(trace);
        traces.ref.t[0].BinaryType = 'Refeyrence';            
    }

    if (trace.BinaryType == 'Min') {           
        traces.min = Object.assign({}, learning);
        traces.min.t = [];
        traces.min.t.push(trace);            
    }

    if (trace.BinaryType == 'Max') {            
        traces.max = Object.assign({}, learning);
        traces.max.t = []
        traces.max.t.push(trace);            
    }

    if (trace.BinaryType == 'Avg') {            
        traces.avg = Object.assign({}, learning);
        traces.avg.t = [];
        traces.avg.t.push(trace);            
    }          
});

console.log("Output",traces);
console.log("Traces- Should be non mutated",traceArr);
console.log("Original",learning.t)

I assume that when I modify the content of the array, the content of the original (learning) object should not getting affected. 我假设修改数组的内容时,原始(学习)对象的内容不会受到影响。

Two questions: 两个问题:

  • I was assuming that traces.ref.t = []; 我假设traces.ref.t = []; should change the reference in the new created object. 应该在新创建的对象中更改引用。 Does it not? 不是吗
  • The console.log("Original",learning.t) output is as below which indicates that the content got changed (text Refeyrence which was modified during array iteration). console.log(“ Original”,learning.t)输出如下所示,它指示内容已更改(在数组迭代期间已修改的文本Refeyrence )。 Why is this happening? 为什么会这样呢? And what should I be doing to avoid it? 我应该怎么做才能避免这种情况?

    'Original' [ { d: 2 , BinaryType: " Refeyrence " }, { d: 3 , BinaryType: "Max" }, { d: 4 , BinaryType: "Avg" }, { d: 5 , BinaryType: "Min" } ] '原始'[{d:2,BinaryType:“ Refeyrence ”},{d:3,BinaryType:“ Max”},{d:4,BinaryType:“ Avg”},{d:5,BinaryType:“ Min” }]

It looks like you're having two issues, but both with mutateing a shared object. 看来您遇到了两个问题,但都与突变共享对象有关。

I was assuming that traces.ref.t = []; 我假设traces.ref.t = []; should change the reference in the new created object. 应该在新创建的对象中更改引用。 Does it not? 不是吗

Doing [] will create a new array instance, but traces.ref is still using the shared traces object at the very start. 进行[]将创建一个新的数组实例,但是traces.ref从一开始仍然使用共享的traces对象。 You likely want to create a copy of the traces object. 您可能想要创建traces对象的副本。 Since it doesn't have any nested values, using spread syntax is an easy way to achieve that in your case: 由于它没有任何嵌套值,因此在您的情况下,使用扩展语法是一种简便的方法:

const newTrace = { ...traces }

The console.log("Original",learning.t) output is as below which indicates that the content got changed (text Refeyrence which was modified during array iteration). console.log(“ Original”,learning.t)输出如下所示,它指示内容已更改(在数组迭代期间已修改的文本Refeyrence)。 Why is this happening? 为什么会这样呢? And what should I be doing to avoid it? 我应该怎么做才能避免这种情况?

This one is because the trace object is being pushed into the array and then it's being modified. 这是因为trace对象被推入数组,然后被修改。 You can work around this by also using an object spread to create a shallow copy: 您还可以使用传播对象创建浅表副本来解决此问题:

newTrace.ref.t.push({ ...trace });

With only these changes, you original code may look like this: 仅进行这些更改,您的原始代码可能如下所示:

let traces = { ref: null, min: null, max: null, avg: null };
let learning = {
  Application: "b3",
  t: [
    {
      d: 2,
      BinaryType: "Current"
    },
    {
      d: 3,
      BinaryType: "Max"
    },
    {
      d: 4,
      BinaryType: "Avg"
    },
    {
      d: 5,
      BinaryType: "Min"
    }
  ]
};

let traceArr = Object.assign([], learning.t);

traceArr.forEach(trace => {
  if (trace.BinaryType == "Current") {
    const newTrace = { ...traces };
    newTrace.ref = Object.assign({}, learning);
    newTrace.ref.t = [];
    newTrace.ref.t.push({ ...trace });
    newTrace.ref.t[0].BinaryType = "Refeyrence";
  }

  if (trace.BinaryType == "Min") {
    const newTrace = { ...traces };
    newTrace.min = Object.assign({}, learning);
    newTrace.min.t = [];
    newTrace.min.t.push({ ...trace });
  }

  if (trace.BinaryType == "Max") {
    const newTrace = { ...traces };
    newTrace.max = Object.assign({}, learning);
    newTrace.max.t = [];
    newTrace.max.t.push({ ...trace });
  }

  if (trace.BinaryType == "Avg") {
    const newTrace = { ...traces };
    newTrace.avg = Object.assign({}, learning);
    newTrace.avg.t = [];
    newTrace.avg.t.push({ ...trace });
  }
});

console.log("Output", traces);
console.log("Traces- Should be non mutated", traceArr);
console.log("Original", learning.t);

Here is this code in the TypeScript playground . 这是TypeScript操场上的代码 It contains a few type errors but this is the minimum number of changes to avoid mutations. 它包含一些类型错误,但这是避免突变的最少更改次数。

I don't think Object.assign() does a deep copy. 我不认为Object.assign()做深拷贝。 I'm pretty sure it's creating a new array whose elements point to the original array. 我很确定它正在创建一个新数组,其元素指向原始数组。 The simplest way to do a deep copy is to convert it to json and back. 进行深层复制的最简单方法是将其转换为json并返回。

let traceArr = JSON.parse(JSON.stringify(learning.t));

The big disadvantage there is that if whatever you're copying has circular references, it won't work. 最大的缺点是,如果您要复制的内容具有循环引用,则它将无法正常工作。

EDIT: 编辑:

To illustrate what's happening, the learning object starts off as 为了说明正在发生的事情,学习对象从

let learning = {
    "Application": "b3",
    "t": [Object1, Object2, ...]
}

Then this happens, 然后发生这种情况

let traceArr = Object.assign([], learning.t);

Which creates a new array and copies the contents of learning.t into it. 这将创建一个新数组,并将learning.t的内容复制到其中。 At this point, traceArr looks like 此时,traceArr看起来像

traceArr = [Object1, Object2, ...]

Then, it loops over the items in traceArr, which are the same objects from learning.t, just pointed to from a different array. 然后,它遍历traceArr中的项目,这些项目是learning.t中的相同对象,只是从另一个数组中指向的。

if (trace.BinaryType == 'Current') {            
    // At this point, trace === Object1

    // Shallow copy of learning
    traces.ref = Object.assign({}, learning);
    // traces.ref = { "Application": "b3", "t": [Object1, Object2, ...] }

    // Create a new array for traces.ref.t
    traces.ref.t = [];
    // traces.ref = { "Application": "b3", "t": [] }

    // Adds Object1 (same object from learning.t) to the new array
    traces.ref.t.push(trace);
    // traces.ref = { "Application": "b3", "t": [Object1] }

    // Modify Object1
    traces.ref.t[0].BinaryType = 'Refeyrence';            
}

If you don't want traces.ret.t's elements to point to the same objects as learning.t, you need to create new objects, ie you need to push a copy of trace instead of trace. 如果您不希望traces.ret.t的元素指向与learning.t相同的对象,则需要创建新对象,即,您需要推送跟踪副本而不是跟踪。 Or, as I suggested, make traceArr a deep copy of learning.t so that all the objects are new and nothing is shared. 或者,按照我的建议,使traceArr成为learning.t的深层副本,以便所有对象都是新对象,并且不共享任何内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM