复制数组的值

2123

在 JavaScript 中将一个数组复制到另一个数组时:

var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d');  // Now, arr1 = ['a','b','c','d']

我意识到 arr2 指的是与 arr1 相同的数组,而不是一个新的独立数组。如何将数组复制以获得两个独立的数组?

4
在Chrome 53和Firefox 48中,slicesplice操作的性能表现很好,而新的扩展运算符和Array.from的实现则比较慢。可参考perfjs.fnfo - Pencroff
https://jsperf.com/flat-array-copy - Walle Cyril
对于包含原始字符串的数组,您可以使用 var arr2 = arr1.splice(); 进行深度复制,但是如果您的数组元素包含文字结构(即 []{})或原型对象(即 function () {}new 等),则此技术将无法工作。请参见下面的答案以获取更多解决方案。 - tim-montague
41
2017年了,你可以考虑使用ES6的特性:let arr2 = [...arr1];。详见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator。 - Hinrich
2
当你声明 a = b; 时,实际上是告诉程序在随机访问内存中指向同一个符号链接。当更改此符号链接处的值时,会影响到 ab... 因此,如果你使用扩展运算符 a= [...b];,程序将创建一个额外的符号链接到随机访问内存中的不同位置,然后你可以独立地操作 ab - 71GA
显示剩余3条评论
40个回答

4

如果您的数组包含基本数据类型(例如 int、char 或 string 等),那么可以使用以下任一方法返回原始数组的副本,如.slice() 或 .map() 或扩展操作符(感谢 ES6)。

new_array = old_array.slice()

或者

new_array = old_array.map((elem) => elem)

或者

const new_array = new Array(...old_array);
但是,如果您的数组包含复杂元素,例如对象(或数组)或更多嵌套对象,那么您必须确保从顶层到最后一级复制所有元素的副本,否则内部对象的引用将被使用,这意味着在new_array中更改object_elements中的值仍将影响old_array。您可以称每个级别的此复制方法为制作深层副本的旧数组。

对于深度复制,您可以使用上述原始数据类型的方法,在每个级别上根据数据类型选择使用或者您可以使用下面提到的昂贵的方法进行深层复制而不做太多工作。

var new_array = JSON.parse(JSON.stringify(old_array));

根据您的需求,有很多其他方法可供使用。我只提到了其中一些,以便概述当我们尝试通过将一个数组复制到另一个数组时会发生什么。


非常感谢,你的答案是唯一一个适用于我的情况的。 - albert sh

4
在我的情况下,我需要确保数组保持完好无损,因此这对我起作用:
// Empty array
arr1.length = 0;
// Add items from source array to target array
for (var i = 0; i < arr2.length; i++) {
    arr1.push(arr2[i]);
}

2
+1 不要在代码中添加模糊性,而是调用一个以不太明显的方式完成相同任务的函数。虽然底层可能使用了slice更高效的方法,但对于任何在代码上工作的人来说,这表明了你的意图。此外,这样做还可以更容易地进行优化,如果你想(例如)过滤你要复制的内容。然而请注意,这种方法不能处理深拷贝,并且相同的内部对象通过引用传递给新数组。这可能是你想要的,也可能不是。 - unsynchronized

4

复制多维数组/对象:

function deepCopy(obj) {
   if (Object.prototype.toString.call(obj) === '[object Array]') {
      var out = [], i = 0, len = obj.length;
      for ( ; i < len; i++ ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   if (typeof obj === 'object') {
      var out = {}, i;
      for ( i in obj ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   return obj;
}

感谢James Padolsey提供这个函数。
来源:这里

3

如果你想创建一个对象或数组的副本,你必须显式地复制对象的属性或数组的元素,例如:

var arr1 = ['a','b','c'];
var arr2 = [];

for (var i=0; i < arr1.length; i++) {
   arr2[i] = arr1[i];
}

您可以在Google上搜索有关不可变原始值和可变对象引用的更多信息。


1
您不必显式复制数组对象的属性。请参阅Chtiwi Malek的答案。 - Magne

3

var arr2 = arr1.slice(0);

这种方式仅适用于 简单数组

如果你有像对象数组这样的 复杂数组,那么你必须使用其他解决方案,例如:

const arr2 = JSON.parse(JSON.stringify(arr1)); 

例如,我们有一个对象数组,每个单元格都有其对象中的另一个数组字段...在这种情况下,如果我们使用slice方法,那么数组字段将通过引用复制,这意味着这些字段的更新将影响原始数组相同的元素和字段。

3

当我们想使用赋值运算符(=)复制一个数组时,它不会创建一个副本,而只是复制指向该数组的指针/引用。例如:

const oldArr = [1,2,3];

const newArr = oldArr;  // now oldArr points to the same place in memory 

console.log(oldArr === newArr);  // Points to the same place in memory thus is true

const copy = [1,2,3];

console.log(copy === newArr);  // Doesn't point to the same place in memory and thus is false

在我们转换数据时,通常希望保留原始数据结构(例如数组)的完整性。为此,我们通过复制数组来进行转换,从而使转换后的数组与初始数组分离。

复制数组的方法:

const oldArr = [1,2,3];

// Uses the spread operator to spread out old values into the new array literal
const newArr1 = [...oldArr];

// Slice with no arguments returns the newly copied Array
const newArr2 = oldArr.slice();

// Map applies the callback to every element in the array and returns a new array
const newArr3 = oldArr.map((el) => el);

// Concat is used to merge arrays and returns a new array. Concat with no args copies an array
const newArr4 = oldArr.concat();

// Object.assign can be used to transfer all the properties into a new array literal
const newArr5 = Object.assign([], oldArr);

// Creating via the Array constructor using the new keyword
const newArr6 = new Array(...oldArr);

// For loop
function clone(base) {
 const newArray = [];
    for(let i= 0; i < base.length; i++) {
     newArray[i] = base[i];
 }
 return newArray;
}

const newArr7 = clone(oldArr);

console.log(newArr1, newArr2, newArr3, newArr4, newArr5, newArr6, newArr7);

当数组或对象嵌套时要注意!

当数组嵌套时,值是通过引用复制的。以下是可能导致问题的示例:

let arr1 = [1,2,[1,2,3]]

let arr2 = [...arr1];

arr2[2][0] = 5;  // we change arr2

console.log(arr1);  // arr1 is also changed because the array inside arr1 was copied by reference

因此,当你想要复制数组中存在对象或数组时,请不要使用这些方法。即仅将这些方法用于基元类型的数组。

如果您确实想要深度克隆JavaScript数组,请结合使用JSON.parseJSON.stringify,像这样:

let arr1 = [1,2,[1,2,3]]

let arr2 = JSON.parse(JSON.stringify(arr1)) ;

arr2[2][0] = 5;

console.log(arr1);  // now I'm not modified because I'm a deep clone

复制的性能:

那么哪种方法可以获得最佳性能呢?结果表明,最冗长的方法,即for循环具有最高性能。对于真正需要占用CPU资源的大型或多个数组的复制,请使用for循环。

之后,.slice() 方法也具有良好的性能,并且更简洁易懂,对程序员来说更容易实现。建议在处理不需要太多占用CPU资源的日常数组复制时使用.slice() 。如果不需要进行深度克隆并且性能是一个问题,则避免使用JSON.parse(JSON.stringify(arr))方法(存在大量开销)。

来源:性能测试


3

structuredClone

在JavaScript中,以值的方式复制数组的现代方法是使用structuredClone

var arr1 = ['a', 'b', 'c'];
var arr2 = structuredClone(arr1); //copy arr1
arr2.push('d');

console.log(arr1); //a,b,c
console.log(arr2); //a,b,c,d


2

对于包含对象的ES6数组

cloneArray(arr) {
    return arr.map(x => ({ ...x }));
}

2
您可以使用ES6扩展运算符来复制数组。
var arr=[2,3,4,5];
var copyArr=[...arr];

2

使用jQuery进行深度复制的方法如下:

var arr2 = $.extend(true, [], arr1);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接