web-dev-qa-db-fra.com

pourquoi une carte js sur un tableau modifie le tableau d'origine?

Je suis assez confus par le comportement de map ().

J'ai un tableau d'objets comme celui-ci:

const products = [{
    ...,
    'productType' = 'premium',
    ...
}, ...]

et je passe ce tableau à une fonction qui devrait renvoyer le même tableau mais avec tous les produits rendus libres:

[{
    ...,
    'productType' = 'free',
    ...
}, ...]

La fonction est:

const freeProduct = function(products){
    return products.map(x => x.productType = "free")
}

Ce qui retourne le tableau suivant:

["free", "free", ...]

Alors j'ai réécrit ma fonction pour être:

const freeProduct = function(products){
    return products.map(x => {x.productType = "free"; return x})
}

Ce qui retourne le tableau comme prévu.

MAIS ! Et c'est le moment où je perds la tête. Dans les deux cas, la gamme de produits d'origine est modifiée.

La documentation autour de map () indique que cela ne devrait pas ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map ).

J'ai même essayé de créer un clone de mon tableau en transformant ma fonction comme ceci

const freeProduct = function(products){
    p = products.splice()
    return p.map(x => {x.productType = "free"; return x})
}

Mais j'obtiens toujours le même résultat (ce qui commence à me rendre fou).

Je serais très reconnaissant à quiconque peut m'expliquer ce que je fais de travers!

Merci

32
Edwin Joassart

Vous ne modifiez pas votre tableau d'origine. Vous modifiez les objets du tableau. Si vous voulez éviter de muter les objets de votre tableau, vous pouvez utiliser Object.assign pour créer un nouvel objet avec les propriétés de l’original, plus les modifications nécessaires:

const freeProduct = function(products) {
  return products.map(x => {
    return Object.assign({}, x, {
      productType: "free"
    });
  });
};

2018 Edit:

Dans la plupart des navigateurs , vous pouvez maintenant utiliser la syntaxe de propagation d'objet au lieu de Object.assign pour y parvenir:

const freeProduct = function(products) {
  return products.map(x => {
    return {
      ...x,
      productType: "free"
    };
  });
};
60
SimpleJ

Pour élaborer sur la réponse de SimpleJ - si vous deviez === les deux tableaux, vous constateriez qu'ils ne seraient pas égaux (pas la même adresse en mémoire), confirmant que le tableau mappé est en fait un nouveau tableau. Le problème est que vous renvoyez un nouveau tableau, qui contient de nombreuses références aux objets SAME du tableau d'origine (il ne renvoie pas de nouveaux objets, mais renvoie des références au même objet). Vous devez donc créer de nouveaux objets qui sont des copies des anciens objets, c’est-à-dire avec l'exemple Object.assign donné par SimpleJ.

12
Geoffrey Abdallah

Malheureusement, que l'opérateur de propagation ou l'opérateur d'affectation d'objet effectue une copie en profondeur ... Vous devez utiliser une fonction semblable à celle de lodash pour obtenir une copie areal et pas seulement une copie de référence.

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!
1
Patrick W.

Iterator de tableau Array.map () crée le nouveau tableau avec le même nombre d'éléments ou ne modifie pas le tableau d'origine. Il peut y avoir un problème de référencement s'il y a un objet dans le tableau car il copie la même référence. Ainsi, lorsque vous apportez des modifications à la propriété de l'objet, la valeur d'origine de l'élément contenant la même référence sera modifiée.

La solution serait de copier l'objet, eh bien, array.Splice() et [...array] (spread Operator) n’aiderait pas dans ce cas, vous pouvez utiliser la bibliothèque d’utilitaires JavaScript telle que Loadash ou simplement utiliser le code de référence ci-dessous:

const newList = JSON.parse(JSON.stringify(orinalArr))
0
Kushal Atreya