Почему 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))