Js Array.prototype.map() 恰好是可变的?

Js Array.prototype.map() happens to be mutable?

为什么 map 方法在 initial purpose 是创建一个新数组时会改变原始数组?

我有一个对象数组,我将其传递给一个纯函数,该函数又映射给定数组和 return 一个新数组。然后我注意到原始数组也发生了变化。我理解 Js 中的对象是通过引用传递的概念,但仍然无法完全理解为什么 map 的实现会改变原始数组,有点打败目的海事组织。

var initialArray = [ { name: 'one' }, { name: 'two' }, { name: 'three'} ];

function doSomething(array) {

  // lodash
  // return _.map(array, (item) => _.assign(item, {isSelected: true}));  

  // vanilla
  return array.map(function(item) {
    item['isSelected'] = true;
    return item
  });

}

var changedArray = doSomething(initialArray);

console.log('initialArray', initialArray); // [{ name: 'one', isSelected: true }, ...]
console.log('changedArray', changedArray); // [{ name: 'one', isSelected: true }, ...]
console.log(initialArray === changedArray); // false

首先我想了解为什么会这样?

其次,我想了解如何在不更改原始数组的情况下映射数组? (即在 map 感觉错误之前每次都做 ._cloneDeep

提前致谢!

编辑

好的,据我了解,事情就是这样。我想我可能出于某种原因有更高的期望,但它在 Js 中是可以解释的,所以至少有一些一致性。

我能想到的用新成员创建新数组的最优雅的解决方案是

return _.map(array, (item) => _.assign({}, ...item, {isSelected: true}));   

.map会新建一个数组,但是数组里面的对象还是会被引用。

因此,当您在 .map 函数中的 object item 中进行更改时,它会引用输入数组中的原始对象。

修复它的一种方法是在修改之前克隆每个对象

var initialArray = [ { name: 'one' }, { name: 'two' }, { name: 'three'} ];

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

function doSomething(array) {

  // lodash
  // return _.map(array, (item) => _.assign(item, {isSelected: true}));  

  // vanilla
  return array.map(function(item) {
    var copy = clone(item);
    copy['isSelected'] = true;
    return copy;
  });

}

var changedArray = doSomething(initialArray);

console.log('initialArray', initialArray); // [{ name: 'one'}, ...]
console.log('changedArray', changedArray); // [{ name: 'one', isSelected: true }, ...]
console.log(initialArray === changedArray); // false

来源:克隆函数是从 this post

复制的

您修改的是通过引用传递给 map 函数的 get 对象,而不是 get 映射的数组。 changedArrayinitialArray 都包含相同的对象。

var initialArray = [ { name: 'one' }, { name: 'two' }, { name: 'three'} ];
var initialArray2 = [ { name: 'one' }, { name: 'two' }, { name: 'three'} ];

function doSomething(array) {
  // vanilla
  return array.map(function(item) {
    item['isSelected'] = true;
    return item
  });

}

function doSomethingElse(array){
  return array.map(function( item ){
    // return a new object don't change the initial one
    return { name: item.name, isSelected: true };
  });
}

var changedArray = doSomething(initialArray),
    differentObjectsInArray = doSomethingElse( initialArray2 );

console.assert( initialArray !== changedArray, 'both arrays are different' );
console.assert( initialArray[0] !== changedArray[0], 'both arrays are referencing different objects' );
console.assert( initialArray2[0] !== differentObjectsInArray[0], 'both arrays are referencing different objects' );
console.log('initialArray', initialArray );
console.log('initialArray2', initialArray2 );
console.log('differentObjectsInArray', differentObjectsInArray );
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>

 var targetArray=JSON.parse(JSON.stringify(souceArray));

如果您只想解决 OP 的示例,那么您可以将 item 对象扩展到从 Array.map().

返回的新对象中

var initialArray = [ { name: 'one' }, { name: 'two' }, { name: 'three'} ];

function doSomething(array) {

  // lodash
  // return _.map(array, (item) => _.assign(item, {isSelected: true}));  

  // vanilla
  return array.map(function(item) {
    return {
      ...item,
      isSelected: true
    }
  });

}

var changedArray = doSomething(initialArray);

console.log('initialArray', initialArray); // initialArray [ { name: 'one' }, { name: 'two' }, { name: 'three' } ] 
console.log('changedArray', changedArray); // changedArray [ { name: 'one', isSelected: true }, { name: 'two', isSelected: true }, { name: 'three', isSelected: true } ] 
console.log(initialArray === changedArray); // false

注意:此解决方案不允许您在不使用扩展运算符的情况下取消引用嵌套在第一层之外的任何对象。

有关 spread 运算符的更多信息,请参见 here