42

我在这里阅读了许多关于“按值”和“按引用”传递将数组发送到 javascript 函数的答案。但是,我在将数组发送到函数并使原始数组保持不变时遇到问题。这个例子说明了这个问题:

function myFunction(someArray)
{
// any function that makes an array based on a passed array;
// someArray has two dimensions;
// I've tried copying the passed array to a new array like this (I've also used 'someArray' directly in the code);

funcArray = new Array();
funcArray = someArray;

var i = 0;

    for(i=0; i<funcArray.length; i++)
    {
    funcArray[i].reverse;
    }

return funcArray;

}

我不明白为什么这个函数中的任何东西都应该改变原始数组。

如果将函数调用分配给新数组,则调用此函数会直接更改原始数组:

myArray = [["A","B","C"],["D","E","F"],["G","H","I"]];
anotherArray = new Array();

anotherArray = myFunction(myArray);
// myArray gets modified!;

我尝试使用 .valueOf() 发送原语:

anotherArray = myFunction(myArray.valueOf());
// myArray gets modified!;

我什至尝试将数组逐个元素和子元素分解,并将所有元素分配给一个新的二维数组,而原始数组仍然会被修改。

我还将子元素加入到一个字符串中,处理它们,将它们拆分回数组,原始数组仍然会被修改。

请问,有人知道我如何将数组值传递给函数而不改变传递的数组吗?

4

9 回答 9

57

在你的函数里面有这个:

funcArray = new Array();
funcArray = someArray;

这实际上不会复制someArray而是引用它,这就是修改原始数组的原因。

您可以使用Array.slice()数组创建所谓的浅表副本。

var funcArray = someArray.slice(0);

原始数组将保持不变,其每个元素仍将引用原始数组中的相应条目。对于“深度克隆”,您需要递归地执行此操作;以下问题讨论了最有效的方法:

在 JavaScript 中深度克隆对象的最有效方法是什么?

顺便说一句,我之前添加varfuncArray。这样做会使它成为函数的局部变量,而不是全局变量。

于 2013-01-23T23:40:47.383 回答
9

制作您可以使用的数组的副本。

一个简单的方法是使用var clone = original.slice(0);

于 2013-01-23T23:42:15.413 回答
3

那么解构赋值(ES6+,检查兼容性)呢?漂亮干净的解决方案。

function myFunction(someArray) {

  for(let i = 0; i < someArray.length; i++)
  {
    someArray[i].reverse();
  }
  
  return someArray;
}

let myArray = [["A","B","C"],["D","E","F"],["G","H","I"]];

// Using destructuring assignment.
// NOTE: We can't just use `[...myArray]` because nested arrays will still be copied by reference.
let anotherArray = myFunction([...myArray.map(nested => [...nested])]);

console.log({original: myArray, copy: anotherArray});

于 2019-02-27T19:42:57.853 回答
3

使用 ES6,您可以使用扩展语法解构直接在参数列表中执行浅拷贝,从而使原始数组保持不变。

请参见下面的示例:

const arr = [1, 2, 3, 4, 5];

function timesTen([...arr]) { // [...arr] shallow copy the array
  for(let i = 0; i < arr.length; i++) {
    arr[i] *= 10; // this would usually change the reference
  }
  return arr;
}

console.log(timesTen(arr));
console.log(arr); // unaltered

于 2019-12-28T02:13:00.407 回答
2

指向数组的变量是对它的引用。当你传递一个数组时,你正在复制这个引用。

您可以使用slice(). 如果你想要一个完整的深度复制,那么在子对象中递归,在复制一些对象时要记住注意事项。

于 2013-01-23T23:41:47.767 回答
1

如果您需要对一个对象执行此操作,请尝试这个花哨的技巧......

MY_NEW_OBJECT = JSON.parse(JSON.stringify(MY_OBJECT));
于 2014-02-20T01:24:48.697 回答
0

一个通用的解决方案是......

// Use the JSON parse to clone the data.
function cloneData(data) {
  // Convert the data into a string first
  var jsonString = JSON.stringify(data);

  //  Parse the string to create a new instance of the data
  return JSON.parse(jsonString);
}

// An array with data
var original = [1, 2, 3, 4];

function mutate(data) {
  // This function changes a value in the array
  data[2] = 4;
}

// Mutate clone
mutate(cloneData(original));

// Mutate original
mutate(original);

这适用于对象和数组。

当您需要深度克隆或者您不知道类型是什么时非常有效。

深度克隆示例...

var arrayWithObjects = [ { id: 1 }, { id: 2 }, { id: 3 } ];

function mutate(data) {
  // In this case a property of an object is changed!
  data[1].id = 4;
}

// Mutates a (DEEP) cloned version of the array
mutate(cloneData(arrayWithObjects));

console.log(arrayWithObjects[1].id) // ==> 2

警告

  • 使用 JSON 解析器进行克隆并不是最高效的选择!

  • 它不克隆函数,只克隆 JSON 支持的数据类型

  • 无法克隆循环引用

于 2013-01-23T23:41:59.930 回答
0

默认情况下,javascript 中除了对象和数组之外,所有内容都是按值复制的,但如果您想对数组使用按值复制:使用 [yourArray].slice(0) 和对象使用 Object.assign(target, . ..来源)

于 2019-12-23T09:30:27.247 回答
-1
var aArray = [0.0, 1.0, 2.0];
var aArrayCopy = aArray.concat();
aArrayCopy[0] = "A changed value.";
console.log("aArray: "+aArray[0]+", "+aArray[1]+", "+aArray[2]);
console.log("aArrayCopy: "+aArrayCopy[0]+", "+aArrayCopy[1]+", "+aArrayCopy[2]);

此答案已被编辑。最初我提出new操作员处理了解决方案,但很快就意识到了这个错误。相反,我选择使用 concat() 方法来创建副本。原来的答案没有显示整个数组,所以错误被无意中隐藏了。下面显示的新输出将证明此答案按预期工作。

aArray: 0, 1, 2
aArrayCopy: A changed value., 1, 2
于 2017-09-25T22:19:00.490 回答