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

时间:2016-02-27 09:05:21

标签: javascript

我有一个名为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分配新值?

3 个答案:

答案 0 :(得分:1)

请参阅map函数文档:

  

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

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

答案 1 :(得分:1)

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
  }) 

答案 2 :(得分:1)

根据您的示例,内联评论:

// 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包含您需要执行的其他数据,则可能需要以某种方式复制对象:How do I correctly clone a JavaScript object?。另一种方法是围绕此类型建模,例如:

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