在 JavaScript 中将一个数组复制到另一个数组时:
var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d'); // Now, arr1 = ['a','b','c','d']
我意识到
arr2
指的是与 arr1
相同的数组,而不是一个新的独立数组。如何将数组复制以获得两个独立的数组?在 JavaScript 中将一个数组复制到另一个数组时:
var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d'); // Now, arr1 = ['a','b','c','d']
arr2
指的是与 arr1
相同的数组,而不是一个新的独立数组。如何将数组复制以获得两个独立的数组?如果您的数组包含基本数据类型(例如 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));
根据您的需求,有很多其他方法可供使用。我只提到了其中一些,以便概述当我们尝试通过 值将一个数组复制到另一个数组时会发生什么。
// 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]);
}
复制多维数组/对象:
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;
}
如果你想创建一个对象或数组的副本,你必须显式地复制对象的属性或数组的元素,例如:
var arr1 = ['a','b','c'];
var arr2 = [];
for (var i=0; i < arr1.length; i++) {
arr2[i] = arr1[i];
}
您可以在Google上搜索有关不可变原始值和可变对象引用的更多信息。
var arr2 = arr1.slice(0);
这种方式仅适用于 简单数组。
如果你有像对象数组这样的 复杂数组,那么你必须使用其他解决方案,例如:
const arr2 = JSON.parse(JSON.stringify(arr1));
当我们想使用赋值运算符(=
)复制一个数组时,它不会创建一个副本,而只是复制指向该数组的指针/引用。例如:
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.parse
和JSON.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))
方法(存在大量开销)。
在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
对于包含对象的ES6数组
cloneArray(arr) {
return arr.map(x => ({ ...x }));
}
var arr=[2,3,4,5];
var copyArr=[...arr];
使用jQuery进行深度复制的方法如下:
var arr2 = $.extend(true, [], arr1);
slice
和splice
操作的性能表现很好,而新的扩展运算符和Array.from
的实现则比较慢。可参考perfjs.fnfo。 - Pencroffvar arr2 = arr1.splice();
进行深度复制,但是如果您的数组元素包含文字结构(即[]
或{}
)或原型对象(即function () {}
、new
等),则此技术将无法工作。请参见下面的答案以获取更多解决方案。 - tim-montaguelet arr2 = [...arr1];
。详见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator。 - Hinricha = b;
时,实际上是告诉程序在随机访问内存中指向同一个符号链接。当更改此符号链接处的值时,会影响到a
和b
... 因此,如果你使用扩展运算符a= [...b];
,程序将创建一个额外的符号链接到随机访问内存中的不同位置,然后你可以独立地操作a
和b
。 - 71GA