【问题标题】:Avoid Mutating JavaScript array避免改变 JavaScript 数组
【发布时间】:2019-09-05 01:45:51
【问题描述】:

下面是我的代码

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)

我假设当我修改数组的内容时,原始(学习)对象的内容不应该受到影响。

两个问题:

  • 我假设 traces.ref.t = [];应该更改新创建的对象中的引用。不是吗?
  • console.log("Original",learning.t) 输出如下 表示内容已更改(文本 Refeyrence 在数组迭代期间修改)。为什么会这样?还有什么 我应该做些什么来避免它?

    '原创' [ { d: 2 , BinaryType:“参考” }, { d: 3 , 二进制类型:“最大” }, { d: 4 , 二进制类型:“平均” }, { d: 5 , 二进制类型:“最小” } ]

【问题讨论】:

    标签: javascript arrays typescript


    【解决方案1】:

    您似乎遇到了两个问题,但都与更改共享对象有关。

    我假设 traces.ref.t = [];应该更改新创建的对象中的引用。不是吗?

    执行[] 将创建一个新的数组实例,但traces.ref 一开始仍使用共享的traces 对象。您可能想要创建traces 对象的副本。由于它没有任何嵌套值,因此在您的情况下使用扩展语法是实现这一目标的一种简单方法:

    const newTrace = { ...traces }
    

    console.log("Original",learning.t) 输出如下,表示内容已更改(在数组迭代期间修改的文本Refeyrence)。为什么会这样?我应该怎么做才能避免它?

    这是因为trace 对象被推入数组,然后被修改。您还可以通过使用对象展开来创建浅拷贝来解决此问题:

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

    仅通过这些更改,您的原始代码可能如下所示:

    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。它包含一些类型错误,但这是避免突变的最少更改次数。

    【讨论】:

    • const newTrace = { ...traces } 无济于事。我希望 traces.min.t = [] 语句帮助我停止引用旧对象并建议 traces.min.t 开始引用这个新数组。否则,您如何更改 javascript 中的引用?记住我有一个 traces.min = Object.assign({}, learning);在我更改参考之前。所以无论如何我都在处理一个全新的对象(浅拷贝)
    • 我不清楚想要的结果是什么。您能否提供您期望最后 3 个控制台日志在您的问题中打印出来的内容?你也可以traces.ref = { ...learning, t: [] }。目前,您正在执行Object.assign({}, learning);,但这是一个浅拷贝(与{ ...learning } 相同),因此t 仍将是同一个数组实例,但执行{ ...learning, t: [] } 将创建一个新的空数组实例
    【解决方案2】:

    我认为 Object.assign() 不会进行深层复制。我很确定它正在创建一个新数组,其元素指向原始数组。进行深拷贝的最简单方法是将其转换为 json 并返回。

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

    最大的缺点是,如果你复制的任何东西都有循环引用,它就不起作用。

    编辑:

    为了说明正在发生的事情,学习对象开始为

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

    然后发生这种情况,

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

    这会创建一个新数组并将 learning.t 的内容复制到其中。此时,traceArr 看起来像

    traceArr = [Object1, Object2, ...]
    

    然后,它遍历 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';            
    }
    

    如果您不希望 traces.ret.t 的元素指向与 learning.t 相同的对象,则需要创建新对象,即您需要推送 trace 的副本而不是 trace。或者,正如我所建议的,将 traceArr 制作为 learning.t 的深层副本,以便所有对象都是新的,并且没有任何内容是共享的。

    【讨论】:

    • 为什么我不能初始化一个空数组?这对我来说是最简单的选择,然后向其中添加元素?
    • 你可以这样做,但它会添加对元素的引用,它不会复制。由于数组中有对象,因此您需要对对象进行深层复制。如果数组是原始数组,比如字符串或整数,那么浅拷贝就足够了。
    • 我更新了我的答案,希望能更好地说明正在发生的事情以及为什么需要对 learning.t 数组进行深度复制。
    猜你喜欢
    • 1970-01-01
    • 2018-07-23
    • 2019-11-07
    • 1970-01-01
    • 1970-01-01
    • 2019-09-24
    • 2012-11-11
    • 2018-05-03
    • 2017-11-11
    相关资源
    最近更新 更多