为什么 map 会改变对象数组?

7
为什么map会改变对象数组?

var obj = {
  items: [{
    value: 1,
    selected: true
  }, {
    value: 2,
    selected: false
  }]
};

var items = obj.items.map(i => {
  if (i.value === 2) i.selected = true;
  return i;
});

console.log(obj);


4
因为它不会复制该对象。 - Federico klez Culloca
1
因为您正在改变对象。map不会创建这些对象的副本。 - deceze
每个 i 仍然是你的 obj.items 数组的一个元素,如果你通过 i.selected = true 改变它,你就改变了这个元素本身。如果你不想要这个行为,只需复制它。 - Ahmed Bajra
@Federico klez Culloca,我在互联网上没有找到任何解释...你是第一个回答的人,你能添加一个答案吗?我会标记它为已接受的。 - terreb
@terreb 的回答已经是正确的了,不需要再添加我的 :) 我建议你将他的标记为正确。 - Federico klez Culloca
这实际上是一个很好的问题。你可能会认为原始数组是不可变的。 - wlwl2
4个回答

7
如果您想快速处理不可变版本的对象数组上的.map,可以使用扩展运算符:

myArrayOfObjects.map(({...obj}) => { });

例子:

const foo = [];

for(let i = 0; i < 5; i++) {
    foo.push({label: "foo"});
}

const bar = foo.map(({...val}) => {
    val.id = Math.random();
  return val;
});

console.log(foo);
console.log(bar);

为什么扩展映射值不会改变原始对象? - Levidps

5

当你对数组进行映射时,它并不会创建对象的副本,而只是遍历数组。

如果你不想改变对象,你必须创建对象的一个副本:

var items = obj.items.map(item => {
    let i = JSON.parse(JSON.stringify(item))
    if (i.value === 2) i.selected = true;
    return i;
});

如果它是一个对象数组,则不会进行复制。如果它是一个整数数组,则会进行复制。 - Federico klez Culloca
是的,但考虑到问题,我认为有必要重申一下。 - Federico klez Culloca

1

.map()Hammerbot所解释的那样,不会创建副本,它会创建一个直接引用您对象的新数组,从而改变您的对象。

如果您不想改变您的对象,可以在映射中使用Object.assign()来创建对象的副本。


2
如果使用“Object.assign”复制的对象中包含对象怎么办?这些子对象仍然是原始数组中内存中的引用。“Object.assign”只进行浅拷贝。 - Modermo

0

首先作为参考...

.map 是一个数组方法,而不是对象方法。 当在数组上适当地使用 .map 方法时,它将返回一个新的数组,其中每个元素都是回调函数的结果。 请查看 MDN Web Doc for Array.prototype.map() 中的第一个示例。 在描述中,它指出 "map() 方法是一种复制方法",这基本上意味着它不会改变原始数组。相反,它将在数组中迭代每个项,执行一些表达式(通过回调函数),然后将结果值作为单个元素返回到新数组中。这并不完全准确,但可以形象地描述。我建议阅读 Copying Methods and Mutating Methodsshallow copy 以更好地理解 "复制" 的工作原理。在遍历完数组的所有元素后,将返回包含新元素值的新数组。

多汁的部分... 现在,尝试在对象上使用map()方法不会创建对象的副本。相反,您将直接引用该对象并直接改变其属性。 当以这种方式处理对象时,您可以利用它的原生方法,如Object.assignObject.keysObject.valuesObject.entries,创建一个数组,然后再利用.map()方法。这里有一篇不错的文章,其中包含一些示例。 另一个有用的项目是for...in语句。您可以利用它来迭代对象的属性,然后执行一些代码来执行检查或修改属性值。以下是一个示例。

let names = ["Superman", "Batman"];

let players = names.map((name) => ({
  Name: name,
  Score: 0,
  IsActivePlayer: false
}));

players.forEach((player) => {
  for (const prop in player) {
    if ((prop === "Name") && (player[prop] === "Superman")) {
      console.log("I do bleed");
      break;
    }
  }
});

一些我建议的最后几点是阅读关于对象字面量的扩展语法的主题。以下是一些补充阅读材料。 对象扩展 vs Object.assign 理解扩展语法

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接