将JavaScript中的数组复制到另一个数组时:
var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d'); //Now, arr1 = ['a','b','c','d']
我意识到arr2
指的是与arr1
相同的数组,而不是一个新的独立数组。如何复制数组以获得两个独立的数组?
答案 0 :(得分:2481)
使用此:
var newArray = oldArray.slice();
基本上,slice()
操作克隆数组并返回对新数组的引用。另请注意:
对于引用,字符串和数字(而不是实际对象),slice()
将对象引用复制到新数组中。原始数组和新数组都引用相同的对象。如果引用的对象发生更改,则更改对新的和原始数组都可见。
字符串和数字等原语是不可变的,因此无法更改字符串或数字。
答案 1 :(得分:460)
在Javascript中,深度复制技术依赖于数组中的元素。我们从那里开始。
元素可以是:文字值,文字结构或原型。
// Literal values (type1)
const booleanLiteral = true;
const numberLiteral = 1;
const stringLiteral = 'true';
// Literal structures (type2)
const arrayLiteral = [];
const objectLiteral = {};
// Prototypes (type3)
const booleanPrototype = new Bool(true);
const numberPrototype = new Number(1);
const stringPrototype = new String('true');
const arrayPrototype = new Array();
const objectPrototype = new Object(); # or "new function () {}"
从这些元素中我们可以创建三种类型的数组。
// 1) Array of literal-values (boolean, number, string)
const type1 = [true, 1, "true"];
// 2) Array of literal-structures (array, object)
const type2 = [[], {}];
// 3) Array of prototype-objects (function)
const type3 = [function () {}, function () {}];
根据数组中元素的类型,我们可以使用各种技术进行深层复制。
文字值数组(type1)
[...myArray]
,myArray.splice(0)
,myArray.slice()
和myArray.concat()
技术可用于仅使用文字值(布尔值,数字和字符串)深度复制数组; Spread运算符[...myArray]
的效果最佳(https://measurethat.net/Benchmarks/Show/4281/0/spread-array-performance-vs-slice-splice-concat)。
文字值数组(type1)和文字结构(type2)
JSON.parse(JSON.stringify(myArray))
技术可用于深层复制文字值(布尔值,数字,字符串)和文字结构(数组,对象),但不能用于原型对象。
所有数组(type1,type2,type3)
jQuery $.extend(myArray)
技术可用于深层复制所有数组类型。像Underscore和Lo-dash这样的库为jQuery $.extend()
提供了类似的深层复制功能,但性能较低。更令人惊讶的是,$.extend()
的效果高于JSON.parse(JSON.stringify(myArray))
技术http://jsperf.com/js-deep-copy/15
对于那些回避第三方库(如jQuery)的开发人员,您可以使用以下自定义函数;它的性能高于$ .extend,并深度复制所有数组。
function copy(aObject) {
if (!aObject) {
return aObject;
}
let v;
let bObject = Array.isArray(aObject) ? [] : {};
for (const k in aObject) {
v = aObject[k];
bObject[k] = (typeof v === "object") ? copy(v) : v;
}
return bObject;
}
问题
var arr1 = ['a','b','c'];
var arr2 = arr1;
我意识到arr2引用的是与arr1相同的数组,而不是a 新的独立阵列。如何复制数组以获得两个 独立数组?
回答
因为arr1
是一个文字值数组(布尔值,数字或字符串),所以您可以使用上面讨论的任何深度复制技术,其中扩展运算符...
具有最高性能。
// Highest performance for deep copying literal values
arr2 = [...arr1];
// Any of these techniques will deep copy literal values as well,
// but with lower performance.
arr2 = arr1.slice();
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = $.extend(true, [], arr1); // jQuery.js needed
arr2 = _.extend(arr1); // Underscore.js needed
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = copy(arr1); // Custom-function needed - as provided above
答案 2 :(得分:176)
您可以使用数组传播...
来复制数组。
const itemsCopy = [...items];
此外,如果想要创建一个新数组,其中现有数组是其中的一部分:
var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
数组点差现在为supported in all major browsers,但如果您需要较旧的支持,请使用typescript或babel并编译为ES5。
答案 3 :(得分:150)
不需要jQuery ...... Working Example
var arr2 = arr1.slice()
将数组从起始位置0
复制到数组末尾。
重要的是要注意它将按原始类型(字符串,数字等)的预期工作,并解释引用类型的预期行为......
如果你有一个引用类型数组,比如类型Object
。数组将被复制,但两个数组都将包含对同一Object
的引用。因此,在这种情况下,即使实际复制了数组,看起来像是通过引用复制数组。
答案 4 :(得分:71)
slice
的替代方法是concat
,可以通过两种方式使用。第一个可能更具可读性,因为预期的行为非常明确:
var array2 = [].concat(array1);
第二种方法是:
var array2 = array1.concat();
科恩(在评论中)指出后一种方法has better performance。
这种方法的工作方式是concat
方法创建一个新数组,该数组由调用它的对象中的元素组成,后跟作为参数传递给它的任何数组的元素。因此,当没有传递参数时,它只是复制数组。
Lee Penkman也在评论中指出,如果array1
有undefined
的可能性,你可以按如下方式返回一个空数组:
var array2 = [].concat(array1 || []);
或者,对于第二种方法:
var array2 = (array1 || []).concat();
请注意,您也可以使用slice
:var array2 = (array1 || []).slice();
。
答案 5 :(得分:52)
这是我在尝试多种方法后完成它的方式:
var newArray = JSON.parse(JSON.stringify(orgArray));
这将创建一个与第一个无关的新深拷贝(不是浅拷贝)。
这显然不会克隆事件和函数,但是你可以在一行中做到这一点,它可以用于任何类型的对象(数组,字符串,数字,对象......)
答案 6 :(得分:19)
一些提到的方法在处理数字或字符串等简单数据类型时效果很好,但是当数组包含其他对象时,这些方法会失败。当我们尝试将任何对象从一个数组传递到另一个数组时,它将作为引用而不是对象传递。
在JavaScript文件中添加以下代码:
Object.prototype.clone = function() {
var newObj = (this instanceof Array) ? [] : {};
for (i in this) {
if (i == 'clone')
continue;
if (this[i] && typeof this[i] == "object") {
newObj[i] = this[i].clone();
}
else
newObj[i] = this[i]
} return newObj;
};
只需使用
即可var arr1 = ['val_1','val_2','val_3'];
var arr2 = arr1.clone()
它会起作用。
答案 7 :(得分:15)
从ES2015开始,
@DataProvder
答案 8 :(得分:14)
我个人认为Array.from是一种更具可读性的解决方案。顺便说一句,请注意它的浏览器支持。
//clone
let x = [1,2,3];
let y = Array.from(x);
//deep clone
let clone = arr => Array.from(arr,item => Array.isArray(item) ? clone(item) : item);
let x = [1,[],[[]]];
let y = clone(x);
答案 9 :(得分:13)
重要!
这里的大多数答案适用于 特定案例 。
如果您不关心深层/嵌套对象和道具的使用( ES6 ):
let clonedArray = [...array]
但是如果要进行深度克隆,请改用此方法:
let cloneArray = JSON.parse(JSON.stringify(array))
对于lodash用户:
let clonedArray = _.clone(array)
documentation
和
let clonedArray = _.cloneDeep(array)
documentation
答案 10 :(得分:11)
如果您所在的环境ECMAScript 6,请使用Spread Operator,您可以这样做:
var arr1 = ['a','b','c'];
var arr2 = [...arr1]; //copy arr1
arr2.push('d');
console.log(arr1)
console.log(arr2)

<script src="http://www.wzvang.com/snippet/ignore_this_file.js"></script>
&#13;
答案 11 :(得分:9)
添加 array.slice(); 的解决方案请注意,如果您有多维数组子数组将被引用复制。 你可以做的是分别循环和切片()每个子数组
var arr = [[1,1,1],[2,2,2],[3,3,3]];
var arr2 = arr.slice();
arr2[0][1] = 55;
console.log(arr2[0][1]);
console.log(arr[0][1]);
function arrCpy(arrSrc, arrDis){
for(elm in arrSrc){
arrDis.push(arrSrc[elm].slice());
}
}
var arr3=[];
arrCpy(arr,arr3);
arr3[1][1] = 77;
console.log(arr3[1][1]);
console.log(arr[1][1]);
同样的事情发生在对象数组中,它们将通过引用复制,你必须手动复制它们
答案 12 :(得分:6)
正如我们在Javascript 数组和对象中所知,但是我们可以通过什么方式复制数组,而不会在以后更改原始数组?
以下几种方法:
想象一下,我们在您的代码中有这个数组:
var arr = [1, 2, 3, 4, 5];
1)在函数中循环遍历数组并返回一个新数组,如下所示:
function newArr(arr) {
var i=0, res = [];
while(i<arr.length){
res.push(arr[i]);
i++;
}
return res;
}
2)使用切片方法,切片用于切片数组的一部分,如果不指定数组的开始和结束,切片将切片的某些部分而不接触切片中的原始部分,它将切片整个数组并基本上制作数组的完整副本,因此我们可以很容易地说:
var arr2 = arr.slice(); // make a copy of the original array
3)同样是联系方法,这是为了合并两个数组,但是我们可以只指定一个数组,然后这基本上复制了新接触数组中的值:
var arr2 = arr.concat();
4)同样使用字符串化和解析方法,不建议这样做,但可以轻松复制数组和对象:
var arr2 = JSON.parse(JSON.stringify(arr));
5)Array.from方法,这在使用前不受广泛支持,请检查不同浏览器中的支持:
const arr2 = Array.from(arr);
6)ECMA6的方式,也没有完全支持,但如果你想要转换,babelJs可以帮助你:
const arr2 = [...arr];
答案 13 :(得分: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的这项功能。
来源:Here
答案 14 :(得分: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]);
}
答案 15 :(得分:4)
var arr2 = new Array(arr1);
现在arr1
和arr2
是存储在不同堆栈中的两个不同的数组变量。
Check this out on jsfiddle
答案 16 :(得分: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.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
循环具有最高的性能。使用for
循环进行真正的CPU密集型复制(大型/许多阵列)。
此后,.slice()
方法也具有不错的性能,并且对于程序员来说也不太冗长和易于实现。我建议每天使用.slice()
来复制不占用大量CPU的阵列。如果不需要深度克隆并且性能成问题,也请避免使用JSON.parse(JSON.stringify(arr))
(开销很大)。
答案 17 :(得分:2)
使用jQuery深层复制可以如下:
var arr2 = $.extend(true, [], arr1);
答案 18 :(得分:2)
您也可以使用ES6传播运算符来复制数组
var arr=[2,3,4,5];
var copyArr=[...arr];
答案 19 :(得分:2)
以下是更多复制方式:
const array = [1,2,3,4];
const arrayCopy1 = Object.values(array);
const arrayCopy2 = Object.assign([], array);
const arrayCopy3 = array.map(i => i);
const arrayCopy4 = Array.of(...array );
&#13;
答案 20 :(得分:2)
如果要创建对象或数组的新副本,则必须显式复制对象的属性或数组的元素,例如:
var arr1 = ['a','b','c'];
var arr2 = [];
for (var i=0; i < arr1.length; i++) {
arr2[i] = arr1[i];
}
您可以在Google上搜索有关不可变原始值和可变对象引用的更多信息。
答案 21 :(得分:2)
快速示例:
var arr1 = ['a','b','c'];
// arr1 and arr2 are independent and primitive elements are stored in
// different places in the memory
var arr2 = arr1.slice();
arr2.push('d');
console.log(arr1); // [ 'a', 'b', 'c' ]
console.log(arr2); // [ 'a', 'b', 'c', 'd' ]
var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
// arr1 and arr2 are independent and reference's/addresses are stored in different
// places in the memory. But those reference's/addresses points to some common place
// in the memory.
var arr2 = arr1.slice();
arr2.pop(); // OK - don't affect arr1 bcos only the address in the arr2 is
// deleted not the data pointed by that address
arr2[0].x = 'z'; // not OK - affect arr1 bcos changes made in the common area
// pointed by the addresses in both arr1 and arr2
arr2[1][0] = 9; // not OK - same above reason
console.log(arr1); // [ { x: 'z', y: 'b' }, [ 9, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]
var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
arr2 = JSON.parse(JSON.stringify(arr1));
arr2.pop(); // OK - don't affect arr1
arr2[0].x = 'z'; // OK - don't affect arr1
arr2[1][0] = 9; // OK - don't affect arr1
console.log(arr1); // [ { x: 'a', y: 'b' }, [ 1, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]
答案 22 :(得分:2)
如果您的数组包含原始数据类型的元素,例如 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。您可以在进行每个级别的复制时称为 DEEP COPY 的old_array。
对于深层复制,您可以根据数据类型在每个级别上对原始数据类型使用上述方法,也可以使用这种代价高昂的方法(如下所述)进行深层复制。复制而无需做很多工作。
var new_array = JSON.parse(JSON.stringify(old_array));
您还可以根据需要使用许多其他方法。我只提到了其中的一些内容,以便大致了解当我们尝试按值将数组复制到另一个 时会发生什么情况。
答案 23 :(得分:1)
答案 24 :(得分:1)
我个人更喜欢这种方式JSON.parse(JSON.stringify(originalObject));
答案 25 :(得分:1)
那么我们如何复制此arr?
let arr = [1,2,3,4,5];
let arrCopy = [...arr];
let arrCopy = arr.slice();
let arrCopy = [].concat(arr);
在诸如“对象/数组”之类的复合值上将一个变量传递给另一个变量的行为不同。在copand值上使用asign运算符,我们将引用传递给对象。这就是为什么在删除/添加arr元素时两个数组的值都改变的原因。
arrCopy[1] = 'adding new value this way will unreference';
当您为变量分配新值时,您是在更改引用本身,并且不会影响原始的对象/数组。
答案 26 :(得分:1)
这是如何对深度可变的图元数组进行处理的方法:
// If a is array:
// then call cpArr(a) for each e;
// else return a
const cpArr = a => Array.isArray(a) && a.map(e => cpArr(e)) || a;
let src = [[1,2,3], [4, ["five", "six", 7], true], 8, 9, false];
let dst = cpArr(src);
答案 27 :(得分:1)
let a = [1,2,3];
现在您可以执行以下任一操作来复制数组。
let b = Array.from(a);
OR
let b = [...a];
OR
let b = new Array(...a);
OR
let b = a.slice();
OR
let b = a.map(e => e);
现在,如果我更改一个,
a.push(5);
然后,a是[1,2,3,5],但b仍然是[1,2,3],因为它具有不同的引用。
但是我认为,在上述所有方法中 Array.from 更好,并且主要用于复制数组。 >
答案 28 :(得分:1)
新推出的Array.from
,但不幸的是,截至撰写本文时,它仅支持最近的Firefox版本(32及更高版本)。它可以简单地使用如下:
var arr1 = [1, 2, 3];
console.log(Array.from(arr1)); // Logs: [1, 2, 3]
参考:Here
或Array.prototype.map
可以与身份功能一起使用:
function identity(param)
{
return param;
}
var arr1 = [1, 2, 3],
clone = arr1.map(identity);
参考:Here
答案 29 :(得分:1)
您可以通过以下方式实现:
arr2 = arr1.map(x => Object.assign({}, x));
答案 30 :(得分:1)
这是一个变体:
var arr1=['a', 'b', 'c'];
var arr2=eval(arr1.toSource());
arr2.push('d');
console.log('arr1: '+arr1+'\narr2: '+arr2);
/*
* arr1: a,b,c
* arr2: a,b,c,d
*/
答案 31 :(得分:0)
对于包含对象的ES6数组
cloneArray(arr) {
return arr.map(x => ({ ...x }));
}
答案 32 :(得分:0)
只写:
method info in object Logger is deprecated (since 2.7.0): Create an instance of via Logger(...) and use the same-named method. Or use SLF4J directly.
您正在使用第一个副本生成新的新数组。请注意,这是将元素推入数组的一种实用方法。
如果您的代码基于ES6,那么您也可以使用散布运算符:
arr2 = arr1.concat();
答案 33 :(得分:0)
当我处理一系列深层嵌套的对象时,这些选项都不对我有用。对于ES6,我发现此解决方案很有帮助。
const old_array = [{name:"Nick", stats:{age:25,height:2}},{name:"June", stats:{age:20,height:2}}];
const new_array = old_array.map(e => {
if (e.name === 'June') {
e = { ...e };
e.stats = {...e.stats, age: 22};
}
return e;
});
只有new_array会受此影响。