【问题标题】:Javascript Array Map unexpectedly changed original array valuesJavascript Array Map 意外更改了原始数组值
【发布时间】:2018-11-28 23:52:49
【问题描述】:

我有创建对象的构造函数

我初始化了一个名为arr1 的数组,调用该函数来生成起始值。

我将arr1 映射到arr2

但是我原来的arr1 被改变了。为什么是这样?是因为我在初始化数组和事件循环时进行了异步回调吗?

作为参考,我试图从我之前关于 canvas here 的帖子中获取想法进行重构

function point(x,y){
  return {x,y}
}

arr1 = [point(1,2), point(3,4)];
console.log(arr1, "arr1");

arr2 = arr1.map(b=>{
  b.x = b.x+2;
  b.y = b.y+2;
  return b;
})
console.log(arr1, "arr1");
console.log(arr2, "arr2");

【问题讨论】:

    标签: javascript arrays


    【解决方案1】:

    在您的 map 回调中,您正在直接更改每个对象 (b) 的属性...

    b.x = b.x+2;
    b.y = b.y+2;
    

    我怀疑你追求的是更不可变的东西

    const arr2 = arr1.map(({x, y}) => ({
      x: x + 2,
      y: y + 2
    }))
    

    这将创建一个具有 +2 值的新数组,而完全不修改原始数组。

    function point(x,y){
      return {x,y}
    }
    
    const arr1 = [point(1,2), point(3,4)];
    console.log('arr1', arr1);
    
    const arr2 = arr1.map(({x, y}) => ({
      x: x + 2,
      y: y + 2
    }))
    
    console.info('arr1', arr1);
    console.info('arr2', arr2);

    【讨论】:

      【解决方案2】:

      当您使用地图时,您会创建一个新数组,但该数组包含对象的引用。因此,当您更改地图中的对象b 时,这是对原始点 的引用,而不是副本。

      function point(x,y){
        return {x,y}
      }
      
      arr1 = [point(1,2), point(3,4)];
      
      arr2 = arr1.map((b, i)=>{
        // b IS on of the objects from arr1
        console.log(`b === arr1[${i}]`, b === arr1[i])
        b.x = b.x+2;
        b.y = b.y+2;
        return b;
      })

      您可以改为创建一个新的point

      function point(x,y){
        return {x,y}
      }
      
      arr1 = [point(1,2), point(3,4)];
      arr2 = arr1.map(({x, y}) => point(x + 2, y + 2))
      
      console.log(arr1, "arr1")
      console.log(arr2, "arr2")

      【讨论】:

        【解决方案3】:

        您看到的行为的原因是 .map() 浅拷贝元素到新数组。

        这一行改变了源数组中每个元素中的xy 值。

        arr2 = arr1.map(b=>{
          b.x = b.x+2;
          b.y = b.y+2;
          return b;
        })
        

        相反,您应该只返回新的 xy 值,而不像这样改变源数组元素

        arr2 = arr1.map(b => {
          return {
            x: b.x + 2,
            y: b.y + 2
          };
        })
        

        您可以尝试下面的 sn-p 来创建一个具有更新的 xy 值的新数组,而无需改变源数组元素。

        function point(x, y) {
          return { x, y }
        }
        
        arr1 = [point(1, 2), point(3, 4)];
        console.log(arr1, "arr1");
        
        arr2 = arr1.map(b => {
          return {
            x: b.x + 2,
            y: b.y + 2
          };
        })
        console.log(arr1, "arr1");
        console.log(arr2, "arr2");

        【讨论】:

          【解决方案4】:

          其他人指出了在您的地图中重新分配 arr 的值的问题,但我想指出您在第一次控制台登录时观察到的副作用 arr1 也正在更新。这是控制台在大多数浏览器中的工作方式的一个限制(有些人认为不是)。如果您在打开并观察控制台日志中的第一个对象之前更改嵌套对象(或数组),它将更新为新值。

          function point(x,y){
            return {x,y}
          }
          
          arr1 = [point(1,2), point(3,4)];
          console.log("arr1 closed", arr1);
          console.log("arr1 opened:", arr1[0], arr1[1]);
          
          arr1[0] = {x: 15, y:42};
          console.log("arr1 closed", arr1);
          console.log("arr1 opened:", arr1[0], arr1[1]);
          

          jsFiddle

          请注意“打开”数组如何显示调用控制台日志时的状态值,但如果您使用嵌套数组展开第一个控制台日志,它会显示更新后的值。

          您不能将此代码作为 sn-p 运行并观察此副作用。它必须在浏览器中运行,才能打印到控制台。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-01-04
            • 2018-03-16
            • 1970-01-01
            • 2021-12-26
            • 1970-01-01
            • 2014-03-18
            相关资源
            最近更新 更多