简体   繁体   English

为什么数组上的js映射会修改原始数组?

[英]Why does a js map on an array modify the original array?

I'm quite confused by the behavior of map().我对 map() 的行为感到很困惑。

I have an array of objects like this:我有一个像这样的对象数组:

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

And I'm passing this array to a function that should return the same array but with all product made free:我将此数组传递给一个函数,该函数应该返回相同的数组,但所有产品都是免费的:

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

The function is:功能是:

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

Which returns the following array:它返回以下数组:

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

So I rewrote my function to be:所以我将我的函数改写为:

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

Which returns the array as intended.它按预期返回数组。

BUT !但 ! And that's the moment where I loose my mind, in both cases my original products array is modified.那是我松懈的时刻,在这两种情况下,我的原始产品数组都被修改了。

Documentation around map() says that it shouldn't ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map ).围绕 map() 的文档说它不应该( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map )。

I even tried to create a clone of my array turning my function into this:我什至尝试创建我的数组的克隆,将我的函数变成这样:

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

But I still get the same result (which starts to drive me crazy).但我仍然得到相同的结果(这开始让我发疯)。

I would be very thankful to anyone who can explain me what I'm doing wrong!我将非常感谢任何可以向我解释我做错了什么的人!

Thank you.谢谢你。

You're not modifying your original array.您没有修改原始数组。 You're modifying the objects in the array.您正在修改数组中的对象。 If you want to avoid mutating the objects in your array, you can use Object.assign to create a new object with the original's properties plus any changes you need:如果您想避免改变数组中的对象,您可以使用Object.assign创建一个具有原始属性的新对象以及您需要的任何更改:

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

2018 Edit: 2018年编辑:

In most browsers you can now use the object spread syntax instead of Object.assign to accomplish this:大多数浏览器中,您现在可以使用对象扩展语法而不是Object.assign来完成此操作:

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

To elaborate on SimpleJ's answer - if you were to === the two arrays, you would find that they would not be equal (not same address in memory) confirming that the mapped array is in fact a new array.详细说明 SimpleJ 的答案 - 如果您要 === 两个数组,您会发现它们不会相等(内存中的地址不同)确认映射的数组实际上是一个新数组。 The issue is that you're returning a new array, that is full of references to the SAME objects in the original array (it's not returning new object literals, it's returning references to the same object).问题是您要返回一个新数组,其中包含对原始数组中相同对象的引用(它不返回新的对象文字,而是返回对同一对象的引用)。 So you need to be creating new objects that are copies of the old objects - ie, w/ the Object.assign example given by SimpleJ.因此,您需要创建作为旧对象副本的新对象 - 即,使用 SimpleJ 给出的 Object.assign 示例。

Unfortunately, whether the spread operator nor the object assign operator does a deep copy.... You need to use a lodash like function to get areal copy not just a reference copy.不幸的是,无论是扩展运算符还是对象赋值运算符都进行了深度复制……您需要使用类似 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!

Array Iterator Array.map() creates the new array with the same number of elements or does not change the original array. Array Iterator Array.map() 使用相同数量的元素创建新数组或不更改原始数组。 There might be the problem with referencing if there is object inside the array as it copies the same reference, so, when you are making any changes on the property of the object it will change the original value of the element which holds the same reference.如果数组中存在对象,因为它复制相同的引用,则引用可能会出现问题,因此,当您对对象的属性进行任何更改时,它将更改包含相同引用的元素的原始值。

The solution would be to copy the object, well, array.Splice() and [...array] (spread Operator) would not help in this case, you can use JavaScript Utility library like Loadash or just use below mention code:解决方案是复制对象,好吧, array.Splice()[...array] (扩展运算符)在这种情况下无济于事,您可以使用 JavaScript 实用程序库,如Loadash或仅使用下面提到的代码:

const newList = JSON.parse(JSON.stringify(orinalArr))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM