在以下情况下,map 如何分配新值

How does map assign the new value in the following case?

本文关键字:分配 新值 何分配 情况下 map      更新时间:2023-09-26

我有一个名为this.list的数组。我想映射它的项目并为这些项目赋予新的价值:

this.list = this.list.map(item => {
  if (item.id === target.id) {
    item.dataX = parseFloat(target.getAttribute('data-x'))
    item.dataY = parseFloat(target.getAttribute('data-y'))
  }
  return item
})

但令我惊讶的是,这也奏效了:

this.list.map(item => {
  if (item.id === target.id) {
    item.dataX = parseFloat(target.getAttribute('data-x'))
    item.dataY = parseFloat(target.getAttribute('data-y'))
  }
  return item
}) 

如果我根本不使用任何赋值,地图怎么会给this.json分配一个新值?

根据您的示例,内联注释:

// The function map returns a _new_ array
// containing all of the existing elements.
// Then is assigned to the existing variable
// overwriting the original array.
this.list = this.list.map(item => {
  // Here "item" refers to the actual object element in the
  // existing list any assignments to this object will affect
  // the existing item in the list.
  if (item.id === target.id) {
    // Here are your assigments.
    item.dataX = parseFloat(target.getAttribute('data-x'));
    item.dataY = parseFloat(target.getAttribute('data-y'));
  }
  // Although the item is returned and would replace the
  // existing element in the new resulting list, the item
  // returned is in fact the actual element that is _already_
  // currently in the list.
  return item;
});
因此,在这种情况下,根据

您分配给原始变量并就地更新原始值的事实,以下内容在结果方面是等效的:

this.list.forEach(item => {
  if (item.id === target.id) {
    item.dataX = parseFloat(target.getAttribute('data-x'));
    item.dataY = parseFloat(target.getAttribute('data-y'));
  }
});

要以不会改变原始对象的方式执行此操作,如果这是您想要的:

var result = this.list.map(item => {
  return target.id === item.id ? {
    id: item.id,
    dataX: parseFloat(target.getAttribute('data-x')),
    dataY: parseFloat(target.getAttribute('data-y'))
  } : item;
});

或者,如果item有需要携带的其他数据,则可能需要以某种方式复制对象:如何正确克隆 JavaScript 对象?一种替代方法是围绕此类型建模,例如:

class Item {
  constructor(id, x, y) {
    this.id = id;
    this.dataX = dataX;
    this.dataY = dataY;
  }
  // Immutability style setter, returns the result
  // as a new instance.
  withData(dataX, dataY) {
    return new Item(this.id, dataX, dataY);
  }
}
var result = this.list.map(item => {
  return item.id === target.id ? item.withData(
    parseFloat(target.getAttribute('data-x')),
    parseFloat(target.getAttribute('data-y'))
  ) : item;
});

在上面的示例中,this.list是未触及的原始数组,其中包含映射操作之前的所有元素。

result包含"更新"元素(与target.id匹配的元素的新实例和不匹配的原始项目)的混合。

关注实例...

如果我们对所有实例进行编号,在第一个示例中的映射操作之前使用 map 并分配回this.list我们可能会有:

this.list
  Array 1
  - Item 1
  - Item 2
  - Item 3
  - Item 4

映射操作后,项目是相同的实例,并且已更新,但数组是不同的实例:

this.list -> map -> this.list
  Array 1             Array 2 (NEW)
  - Item 1       ->   - Item 1
  - Item 2       ->   - Item 2
  - Item 3       ->   - Item 3
  - Item 4       ->   - Item 4

forEach示例中,在forEach操作之前和结果相同之后,实例都已就地更新:

this.list (forEach) this.list (No change)
  Array 1             Array 1
  - Item 1            - Item 1
  - Item 2            - Item 2
  - Item 3            - Item 3
  - Item 4            - Item 4

在每个不可变示例中,this.list与以前相同,但result将是不同的数组实例,匹配的项目将是不同的实例,在以下示例中Item 1已匹配和更新:

this.list -> map -> result             this.list (Untouched)
  Array 1             Array 2 (NEW)      Array 1
  - Item 1            - Item 5 (NEW)     - Item 1
  - Item 2       ->   - Item 2           - Item 2
  - Item 3       ->   - Item 3           - Item 3
  - Item 4       ->   - Item 4           - Item 4

参考map函数文档:

map 不会改变调用它的数组(尽管回调如果调用,可能会这样做)。

请注意,您在callback中使用的item对象是对实际this.list元素的引用,在您的情况下,您正在更改item对象。

JavaScript 将对象存储为引用,因此当您映射数组时,您正在更改原始对象。您可以在下面的代码中看到相同的行为:

let original = [{x: 5}];
let copied = original;
copied[0].x = 6;
console.log(original[0].x); // 6

您必须重新创建对象或以某种方式克隆它。

  this.list = this.list.map(item => {
    if (item.id === target.id) {
      item = {
        id: target.id,
        dataX: parseFloat(target.getAttribute('data-x')),
        dataY: parseFloat(target.getAttribute('data-y')),
        ...
      };
    }
    return item
  })