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

Edw*_*art 34 javascript dictionary functor node.js

我对map()的行为很困惑.

我有一个这样的对象数组:

const products = [{
    ...,
    'productType' = 'premium',
    ...
}, ...]
Run Code Online (Sandbox Code Playgroud)

我将这个数组传递给一个函数,该函数应返回相同的数组,但所有产品都是免费的:

[{
    ...,
    'productType' = 'free',
    ...
}, ...]
Run Code Online (Sandbox Code Playgroud)

功能是:

const freeProduct = function(products){
    return products.map(x => x.productType = "free")
}
Run Code Online (Sandbox Code Playgroud)

返回以下数组:

["free", "free", ...]
Run Code Online (Sandbox Code Playgroud)

所以我重写了我的功能:

const freeProduct = function(products){
    return products.map(x => {x.productType = "free"; return x})
}
Run Code Online (Sandbox Code Playgroud)

它按预期返回数组.

但是!那是我放松心情的时刻,在这两种情况下我的原始产品阵列都被修改了.

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})
}
Run Code Online (Sandbox Code Playgroud)

但我仍然得到相同的结果(这开始让我发疯).

我会非常感谢任何可以解释我做错的人!

谢谢

Sim*_*leJ 63

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

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

2018编辑:

大多数浏览器中,您现在可以使用对象扩展语法而不是Object.assign完成此操作:

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

  • 它不是真的不合逻辑,因为数组不包含对象,它包含对象的引用.引用保持不变,因此原始数组也是如此.被引用的对象是已被修改的对象.想想书中的单词,语言随着时间的推移而演变,因此单词的含义可能已经改变,但书本身也是如此. (6认同)

Geo*_*lah 13

详细说明SimpleJ的答案 - 如果你是= =两个数组,你会发现它们不相等(内存中的地址不同),确认映射的数组实际上是一个新的数组.问题是你正在返回一个新数组,它充满了对原始数组中SAME对象的引用(它没有返回新的对象文字,它返回对同一对象的引用).因此,您需要创建作为旧对象副本的新对象 - 即,使用SimpleJ给出的Object.assign示例.


小智 5

不幸的是,无论扩展运算符还是对象分配运算符都执行深层复制......您需要使用类似 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!
Run Code Online (Sandbox Code Playgroud)