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