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

Javascript 为什么数组上的js映射会修改原始数组?,javascript,node.js,dictionary,functor,Javascript,Node.js,Dictionary,Functor,我对map()的行为感到非常困惑 我有这样一个对象数组: const products = [{ ..., 'productType' = 'premium', ... }, ...] 我将这个数组传递给一个函数,该函数应该返回相同的数组,但所有产品都是免费的: [{ ..., 'productType' = 'free', ... }, ...] 功能是: const freeProduct = function(products){

我对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()的文档说它不应该()

我甚至尝试创建我的阵列的克隆,将我的函数转换为:

const freeProduct = function(products){
    p = products.splice()
    return p.map(x => {x.productType = "free"; return x})
}
但我还是得到了同样的结果(这让我开始发疯)

我将非常感谢任何能解释我做错了什么的人


谢谢。

您没有修改原始阵列。您正在修改阵列中的对象。如果要避免对数组中的对象进行变异,可以使用创建具有原始属性的新对象以及所需的任何更改:

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"
    };
  });
};

要详细说明SimpleJ的答案,如果您要===这两个数组,您会发现它们不相等(内存中的地址不相同),从而确认映射的数组实际上是一个新数组。问题是您返回的是一个新数组,其中充满了对原始数组中相同对象的引用(它不是返回新对象文本,而是返回对相同对象的引用)。因此,您需要创建新的对象,这些对象是旧对象的副本-例如,w/Object.assign示例由SimpleJ给出。

不幸的是,无论spread操作符还是Object assign操作符是否执行深度复制。。。。您需要使用类似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.map()创建具有相同元素数的新数组,或者不更改原始数组。如果数组中存在复制相同引用的对象,则引用可能会出现问题,因此,当您对对象的属性进行任何更改时,它将更改包含相同引用的元素的原始值

解决方案是复制对象,
array.Splice()
[…array]
(扩展运算符)在这种情况下没有帮助,您可以像使用JavaScript实用程序库一样使用,或者只使用下面提到的代码:

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

@这是ES6中添加的胖箭头函数语法。在MDN上有一些关于它的信息。Javascript假装是一种函数式语言,但就像class关键字一样,这很混乱,不直观,而且浪费了很多时间<代码>您没有修改原始数组。您正在修改数组中的对象——虽然这个答案是正确的,但这是不合逻辑的,因为在现实世界中,您无法更改某些内容的某些部分并使其保持不变。这并不是真的不合逻辑,因为数组不包含对象,它包含对对象的引用。引用保持不变,因此原始数组也保持不变。被引用的对象是已修改的对象。想想书中的单词,语言会随着时间的推移而演变,因此单词的含义可能会发生变化,但书本身是一样的。
existingArray.filter(e=>e).map(//随便你要什么)
我使用上面的代码来实现内联解决方案,这在大多数情况下比克隆更快。由于
filter
函数确实提供了一个新数组,因此它不会修改现有数组中的任何内容。@Asqan在您发布的代码中,
.filter(e=>e)
只会生成一个新数组,其中删除了false(
null
0
未定义的
,等等)值。Map还会生成一个新数组,因此除非您想过滤掉错误的值,否则该过滤器在这里没有用处。