为啥修改 array.slice() 也会改变原始数组?

Posted

技术标签:

【中文标题】为啥修改 array.slice() 也会改变原始数组?【英文标题】:Why modifying array.slice() also changes the original array?为什么修改 array.slice() 也会改变原始数组? 【发布时间】:2019-07-27 15:34:48 【问题描述】:

我正在尝试修改复制的数组而不更改原始数组。这是我尝试过的,通过使用slice() 方法,但它没有按预期工作:

//toFill holds the values I want to add into an array named secondArr, 
//replace every empty sting within secondArr with 0
var toFill = [0, 0, 0, 0];
var mainArr = [
  [" ", 1, 1, 1],
  [" ", 1, 1, 1],
  [" ", 1, 1, 1],
  [" ", 1, 1, 1]
];
var secondArr = mainArr.slice(0,4);
//this function returns a 1D array, stores the indices of all empty strings within an array
function findBlankSpaces(secondArr) 
  var emptyIndices = [];
  var innerArrLen = secondArr.length;
  var outterArrLen = secondArr[0].length;
  for (var i = 0; i < innerArrLen; i++) 
    for (var j = 0; j < outterArrLen; j++) 
      if (secondArr[i][j] == " ") 
        emptyIndices.push([i, j]);
      
    
  
  return emptyIndices;


//this function returns the modified array, with empty spaces replaced with 0s
function fillWithZero(secondArr, toFill) 
  var emptyIndices = findBlankSpaces(secondArr);
  for (var i = 0; i < emptyIndices.length; i++) 
    secondArr[emptyIndices[i][0]][emptyIndices[i][1]] = toFill[i];
  

//consoles
console.log(fillWithZero(secondArr, toFill));
console.log(mainArr);
//expected output in console is [[" ", 1,1,1], [" ",1,1,1], [" ",1,1,1], [" ",1,1,1]];
//actual output is [[0,1,1,1], [0,1,1,1], [0,1,1,1], [0,1,1,1]];
//I didn't modify mainArr, but only modified secondArr, why that mainArr also affected?

我没有修改mainArr,只是使用slice()创建了一个副本,但是为什么它的副本变化时它一直在变化?

这个问题是: 有什么方法可以阻止这种情况发生,或者我如何再次调用 mainArr 而没有任何 0,我希望 mainArr 保持不变。谢谢

【问题讨论】:

"我没有修改mainArr,只是使用slice()创建了一个副本,但是为什么它的副本变化时它一直在变化?”参考.slice():“slice() 方法将数组的一部分的浅拷贝返回到从开始到结束(不包括结束)选择的新数组对象中。原始数组不会被修改。”您需要指定索引(arr.slice([begin[, end]]) 创建副本时,数组和对象始终具有对父对象的引用。所以当发生变化时,它会影响两者 这样做:var secondArr = JSON.parse(JSON.stringify(mainArr)); 【参考方案1】:

slice() 方法将数组的一部分的浅拷贝返回到从开始到结束(不包括结束)选择的新数组对象中,其中开始和结束表示该数组中项目的索引。原数组不会被修改。

请参考此链接:

https://developer.mozilla.org/en-US/docs/Web/javascript/Reference/Global_Objects/Array/slice

【讨论】:

【参考方案2】:

Slice MDN 所示,slice 方法返回调用它的数组的浅表副本。 Deep copy 讨论了几种在 JS 中深度复制对象的方法。它提到的一个很好的是

var secondArr = JSON.parse(JSON.stringify(mainArr))

这会将您的数组转换为 JSON,然后将其解析为新对象

【讨论】:

【参考方案3】:

如果您将 slice 方法应用于包含数组的数组,则子数组将通过引用进行复制。您添加到副本中的任何内容都将添加到包含数组的原始数组中。

【讨论】:

slice() 方法将数组的一部分的浅拷贝返回到从开始到结束(不包括结束)选择的新数组对象中,其中开始和结束表示该数组中项目的索引.原数组不会被修改。【参考方案4】:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice

slice() 方法将数组的一部分的浅拷贝返回到一个新的数组对象中

这里的关键词是shallow

mainArr 是一个数组数组,所以虽然slice 会给你一个外部数组的新副本,但它给你的数组包含对mainArray 中项目的引用,所以当你修改它们时,你实际上是修改mainArray中的项目。

您可以深度克隆 mainArr,但只替换您需要的位可能会更有效。

这很有效,尽管它可以变得更干净。一种选择是使用immer 进行修改。

//replace every empty sting within secondArr with 0
var toFill = [0, 0, 0, 0];
var mainArr = [[" ", 1, 1, 1], [" ", 1, 1, 1], [" ", 1, 1, 1], [" ", 1, 1, 1]];
//this function returns a 1D array, stores the indices of all empty strings within an array
function findBlankSpaces(arr) 
  var emptyIndices = [];
  var innerArrLen = arr.length;
  var outterArrLen = arr[0].length;
  for (var i = 0; i < innerArrLen; i++) 
    for (var j = 0; j < outterArrLen; j++) 
      if (arr[i][j] == " ") 
        emptyIndices.push([i, j]);
      
    
  
  return emptyIndices;


//this function returns the modified array, with empty spaces replaced with 0s
function fillWithZero(mainArr, toFill) 
  var emptyIndices = findBlankSpaces(mainArr);
  let newArr = [...mainArr];
  emptyIndices.forEach(indic => 
    newArr[indic[0]] = [
      ...mainArr[indic[0]].slice(0, indic[1]),
      toFill[indic[1]],
      ...mainArr[indic[0]].slice(indic[1] + 1)
    ];
  );
  return newArr;

//consoles
console.log(fillWithZero(mainArr, toFill));
console.log(mainArr);

【讨论】:

【参考方案5】:

Arrayobject 在制作副本时始终具有对父级的引用。 也就是说,当我们将array a 复制到array b 时,array b 中所做的更改也会影响array a,因为b 引用了a

为了解决这个问题,我们可以使用扩展运算符 ( ... )

请检查下面的sn-p

var a = [1,2,3,4,5,6];
var b = a;
console.log('First array ', a);
console.log('Second array ', b);
// lets remove an element from b and check
b.splice(1,1);
console.log('Second array after splicing', b);
console.log('First array after splicing', a);
// Here value in the index 1 is missing in both the array.
// to prevent this we can use spread operator


var a = [1,2,3,4,5,6];
var b = [...a];
console.log('First array ', a);
console.log('Second array ', b);
// lets remove an element from b and check
b.splice(1,1);
console.log('Second array after splicing using spread operator', b);
console.log('First array after splicing using spread operator', a);

【讨论】:

重要的是要注意... 扩展运算符也只会制作浅拷贝。你可以在这里看到例子***.com/questions/54605286/…

以上是关于为啥修改 array.slice() 也会改变原始数组?的主要内容,如果未能解决你的问题,请参考以下文章

关于在函数中使用Array.prototype.slice.call而不是直接用slice

通过引用传递;为啥原始对象没有改变?

数组的12种拷贝技巧

为啥选择后鼠标悬停回到原始颜色

为啥在Javascript中将一个对象分配给另一个变量也会改变初始对象[重复]

为啥修改集合也会修改 foreach 中的前一个集合