【问题标题】:Why does a js map on an array modify the original array?为什么数组上的js映射会修改原始数组?
【发布时间】:2016-06-25 15:33:16
【问题描述】:

我对 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})
}

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

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

谢谢。

【问题讨论】:

    标签: javascript node.js dictionary functor


    【解决方案1】:

    您没有修改原始数组。您正在修改数组中的对象。如果您想避免改变数组中的对象,可以使用Object.assign 创建一个新对象,其中包含原始属性以及您需要的任何更改:

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

    2018 年编辑:

    most browsers 中,您现在可以使用对象扩展语法而不是Object.assign 来完成此操作:

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

    【讨论】:

    • @HarshaKanchina 这是 ES6 中添加的胖箭头函数语法。 Here 在 MDN 上有一些关于它的信息。
    • Javascript 伪装成一种函数式语言,但就像 class 关键字一样,这很混乱、不直观并且浪费大量时间。 You're not modifying your original array. You're modifying the objects in the array - 虽然这个答案是正确的,但它是不合逻辑的,因为在现实世界中你不能改变某些东西的某些部分并让它保持不变。
    • 这并不是不合逻辑的,因为数组不包含对象,它包含对对象的引用。引用保持不变,因此原始数组也是如此。被引用的对象是被修改的对象。想想书中的单词,语言会随着时间的推移而演变,因此单词的含义可能已经改变,但书本身是一样的。
    • existingArray.filter(e => e).map( // whatever you want ) 我将上面的代码用于内联解决方案,这对我来说在大多数情况下比克隆更快。由于filter 函数确实提供了一个新数组,因此它不会修改现有数组中的任何内容。
    • @Asqan 在您发布的代码中,.filter(e => e) 只是生成一个新数组,其中删除了 falsey(null0undefined 等)值。 Map 还会生成一个新数组,因此除非您想过滤掉错误值,否则过滤器在这里没有用处。
    【解决方案2】:

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

    【讨论】:

      【解决方案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 Iterator Array.map() 创建具有相同数量元素的新数组或不更改原始数组。如果数组内有对象复制相同的引用,则引用可能会出现问题,因此,当您对对象的属性进行任何更改时,它将更改包含相同引用的元素的原始值。

        解决方案是复制对象,好吧,array.Splice()[...array](spread Operator) 在这种情况下无济于事,您可以使用 JavaScript 实用程序库,如 Loadash 或只使用下面提到的代码:

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

        【讨论】:

          猜你喜欢
          • 2019-07-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-28
          • 2021-04-01
          • 2021-11-18
          • 2020-10-04
          • 1970-01-01
          相关资源
          最近更新 更多