Почему js-карта на массиве изменяет исходный массив?
Я довольно смущен поведением map().
У меня есть массив таких объектов:
const products = [{
...,
'productType' = 'premium',
...
}, ...]
и я передаю этот массив функции, которая должна возвращать тот же массив, но со всем свободным продуктом:
[{
...,
'productType' = 'free',
...
}, ...]
Функция:
const freeProduct = function(products){
return products.map(x => x.productType = "free")
}
Что возвращает следующий массив:
["free", "free", ...]
Итак, я переписал свою функцию:
const freeProduct = function(products){
return products.map(x => {x.productType = "free"; return x})
}
Возвращает массив по назначению.
НО! И тот момент, когда я теряю сознание, в обоих случаях изменен мой исходный массив продуктов.
Документация вокруг map() говорит, что она не должна (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
Я даже попытался создать клон моего массива, который превратил мою функцию следующим образом
const freeProduct = function(products){
p = products.splice()
return p.map(x => {x.productType = "free"; return x})
}
Но я все равно получаю тот же результат (который начинает сводить меня с ума).
Я был бы очень благодарен всем, кто может объяснить мне, что я делаю неправильно!
Спасибо
Ответы
Ответ 1
Вы не изменяете свой исходный массив. Вы модифицируете объекты в массиве. Если вы хотите избежать изменения объектов в вашем массиве, вы можете использовать Object.assign
чтобы создать новый объект с исходными свойствами плюс любые необходимые вам изменения:
const freeProduct = function(products) {
return products.map(x => {
return Object.assign({}, x, {
productType: "free"
});
});
};
2018 Изменить:
В большинстве браузеров вы можете теперь использовать синтаксис распространения объекта вместо Object.assign
для выполнения этого:
const freeProduct = function(products) {
return products.map(x => {
return {
...x,
productType: "free"
};
});
};
Ответ 2
Чтобы проработать ответ SimpleJ - если бы вы были === из двух массивов, вы обнаружили бы, что они не будут равны (не тот же адрес в памяти), подтверждающий, что сопоставленный массив на самом деле является новым массивом. Проблема в том, что вы возвращаете новый массив, полный ссылок на объекты SAME в исходном массиве (он не возвращает новые литералы объектов, возвращая ссылки на один и тот же объект). Поэтому вам нужно создавать новые объекты, которые являются копиями старых объектов, т.е. С примером Object.assign, заданным SimpleJ.
Ответ 3
К сожалению, независимо от того, выполняет ли оператор распространения или оператор присваивания объекта глубокое копирование... Вам нужно использовать функцию типа lodash, чтобы получить ареальную копию, а не просто эталонную копию.
const util = require('util');
const print = (...val) => {
console.log(util.inspect(val, false, null, false /* enable colors */));
};
const _ = require('lodash');
const obj1 = {foo:{bar:[{foo:3}]}};
const obj2 = {foo:{bar:[{foo:3}]}};
const array = [obj1, obj2];
const objAssignCopy = x => { return Object.assign({}, x, {})};
const spreadCopy = x => { return {...x}};
const _Copy = x => _.cloneDeep(x);
const map1 = array.map(objAssignCopy);
const map2 = array.map(spreadCopy);
const map3 = array.map(_Copy);
print('map1', map1);
print('map2', map2);
print('map3', map3);
obj2.foo.bar[0].foo = "foobar";
print('map1 after manipulation of obj2', map1); // value changed
print('map2 after manipulation of obj2', map2); // value changed
print('map3 after manipulation of obj2', map3); // value hasn't changed!
Ответ 4
Итератор массива Array.map() создает новый массив с тем же количеством элементов или не меняет исходный массив. Может возникнуть проблема со ссылками, если внутри массива есть объект, поскольку он копирует одну и ту же ссылку, поэтому, когда вы вносите какие-либо изменения в свойство объекта, он изменит исходное значение элемента, который содержит ту же ссылку.
Решение состоит в том, чтобы скопировать объект, ну, array.Splice()
, array.Splice()
и [...array]
(оператор распространения) не помогут в этом случае, вы можете использовать библиотеку JavaScript Utility, такую как Loadash, или просто использовать нижеприведенный код:
const newList = JSON.parse(JSON.stringify(orinalArr))