Copy array by value
将javascript中的数组复制到另一个数组时:
1 2 3 | var arr1 = ['a','b','c']; var arr2 = arr1; arr2.push('d'); //Now, arr1 = ['a','b','c','d'] |
我意识到,
使用此:
1 | var newArray = oldArray.slice(); |
基本上,
对于引用、字符串和数字(而不是实际对象),
字符串和数字等基元是不可变的,因此不可能更改字符串或数字。
在JavaScript中,深度复制技术依赖于数组中的元素。我们从那里开始吧。
三种元素元素可以是:文本值、文本结构或原型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 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 2 3 4 5 6 7 8 | // 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() 技术只能用于深度复制具有文字值(布尔值、数字和字符串)的数组;其中,扩展运算符[...myArray] 的性能最好(https://measurethat.net/bencheps/show/4281/0/扩展数组性能与切片拼接concat)。文本值(type1)和文本结构(type2)的数组
JSON.parse(JSON.stringify(myArray)) 技术可用于深度复制文本值(布尔值、数字、字符串)和文本结构(数组、对象),但不能复制原型对象。所有数组(类型1、类型2、类型3)jquery
$.extend(myArray) 技术可用于深度复制所有数组类型。像下划线和lo-dash这样的库提供类似于jquery$.extend() 的深度复制功能,但性能较低。更令人惊讶的是,$.extend() 的性能比JSON.parse(JSON.stringify(myArray)) 技术(http://jspef.com/js-deep-copy/15)更高。对于那些不愿使用第三方库(如jquery)的开发人员,您可以使用下面的定制函数;它的性能高于$.extend,并且可以深度复制所有数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; } |
所以要回答这个问题…
问题
1 2 | var arr1 = ['a','b','c']; var arr2 = arr1; |
I realized that arr2 refers to the same array as arr1, rather than a
new, independent array. How can I copy the array to get two
independent arrays?
回答
由于
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 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 |
可以使用array spreads
另外,如果要创建一个新数组,其中现有数组是其中的一部分:
1 2 | var parts = ['shoulders', 'knees']; var lyrics = ['head', ...parts, 'and', 'toes']; |
现在,所有主要浏览器都支持数组排列,但如果需要较旧的支持,请使用typescript或babel并编译为ES5。
有关价差的更多信息
不需要jquery…工作实例
1 | var arr2 = arr1.slice() |
这会将数组从起始位置
重要的是要注意,对于基元类型(字符串、数字等),它将按预期工作,并解释引用类型的预期行为…
如果您有一个引用类型数组,例如
替代
1 | var array2 = [].concat(array1); |
第二种方法是:
1 | var array2 = array1.concat(); |
科恩(在评论中)指出,后一种方法具有更好的性能。
这样做的方法是,
Lee Penkman也在评论中指出,如果
1 | var array2 = [].concat(array1 || []); |
或者,对于第二种方法:
1 | var array2 = (array1 || []).concat(); |
请注意,您也可以使用
这就是我在尝试了很多方法之后所做的:
1 | var newArray = JSON.parse(JSON.stringify(orgArray)); |
这将创建与第一个深度副本(不是浅副本)无关的新深度副本。
而且,这显然不会克隆事件和函数,但是很好,您可以在一行中完成它,并且它可以用于任何类型的对象(数组、字符串、数字、对象…)
上面提到的一些方法在处理简单的数据类型(如数字或字符串)时工作得很好,但是当数组包含其他对象时,这些方法会失败。当我们试图将任何对象从一个数组传递到另一个数组时,它将作为引用传递,而不是作为对象传递。
在javascript文件中添加以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 | 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; }; |
简单使用
1 2 | var arr1 = ['val_1','val_2','val_3']; var arr2 = arr1.clone() |
它会起作用的。
从ES2015
1 | var arr2 = [...arr1]; |
我个人认为array.from是一个更易读的解决方案。顺便说一句,小心它的浏览器支持。
1 2 3 4 5 6 7 8 | //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); |
是重要的。 P / < >
现在的工程为例具体回答的睾丸。 P / < >
如果你不在乎深/一个嵌套的物体和props使用(es6): P / < >
但是,如果你想做深克隆用这个代替: P / < >
为lodash用户: P / < >
和 P / < >
如果您处于ecmascript 6的环境中,使用spread操作符可以这样做:
1 2 3 4 5 6 | var arr1 = ['a','b','c']; var arr2 = [...arr1]; //copy arr1 arr2.push('d'); console.log(arr1) console.log(arr2) |
1 | <script src="http://www.wzvang.com/snippet/ignore_this_file.js"> |
添加到array.slice()的解决方案中;请注意,如果有多维数组,则子数组将被引用复制。您可以分别循环和切片()每个子数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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]); |
同样的事情也会发生在对象数组中,它们将被引用复制,您必须手动复制它们。
我们知道在javascript arrays和物体是由参考的方式,但什么是我们可以做的没有改变原有的拷贝的阵列的阵列以后的一个吗? P / < >
这里是一些的方式做它。 P / < >
假设我们有一个这样的阵列在你的代码: P / < >
1 | var arr = [1, 2, 3, 4, 5]; |
1)looping通过在阵列的功能和回报的新的阵列,像这样: P / < >
1 2 3 4 5 6 7 8 | function newArr(arr) { var i=0, res = []; while(i<arr.length){ res.push(arr[i]); i++; } return res; } |
2)使用的切片的切片的方法,也为slicing部分的阵列,它将日常的一些部分的阵列没有touching之原,在的切片,如果不要specify开始和结束的阵列,它将整个阵列的切片和basically做出完整的拷贝的阵列,所以我们可以易说: P / < >
1 | var arr2 = arr.slice(); // make a copy of the original array |
3)也接触的方法,这为合并两个阵列,但我们不能只是一个specify arrays,然后让这basically拷贝的* *的值在纽约contacted阵列: P / < >
1 | var arr2 = arr.concat(); |
4)也stringify和parse法,是不推荐的,但可以成为一个简单的方式来复制和阵列的物体: P / < >
1 | var arr2 = JSON.parse(JSON.stringify(arr)); |
5 array.from)的方法,这是不widely supported,使用之前检查一下支持在不同的browsers: P / < >
1 | const arr2 = Array.from(arr); |
6 ecma6)的方式,也不全supported,但babeljs可以帮助你,如果你想transpile: P / < >
1 | const arr2 = [...arr]; |
复制多维数组/对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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提供此功能。
来源:这里
在我的特殊情况下,我需要确保阵列保持完整,因此这对我有效:
1 2 3 4 5 6 | // 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]); } |
丹,不需要使用花哨的tricks。所有你需要做的是让拷贝的arr1做这。 P / < >
1 | var arr2 = new Array(arr1); |
P / < >
现在
当我们要复制一个阵列使用的分配算子(
1 2 3 4 5 6 7 8 9 | 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 |
P / < >
当我们often transform数据我们要保持我们的初始datastructure(例如阵列intact)。我们这样做,由制作的精确拷贝的阵列,所以我们可以在这一transformed的初始intact一停止,风格。 P / < > 的方式copying of an阵列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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); |
P / < > 小心点,当arrays或物体是一个嵌套的。:
当arrays是一个嵌套的值是copied by参考。这里是一个例子的技术,这可能导致到的问题: P / < >
1 2 3 4 5 6 7 | 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 |
P / < >
所以不要使用这些方法,当有物体或在你的arrays阵列你想要拷贝的。公元前使用这些方法在arrays of primitives只。 P / < >
如果你想做的deepclone javascript阵列使用
1 2 3 4 5 6 7 | 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 |
P / < > 性能:copying
所以,我们做一个选择为最佳的性能。它变了,现在verbose方法,
之后,
源代码的性能试验 P / < >
你也可以使用es6传播算子拷贝到阵列 P / < >
1 2 | var arr=[2,3,4,5]; var copyArr=[...arr]; |
如果要创建对象或数组的新副本,必须显式复制对象或数组元素的属性,例如:
1 2 3 4 5 6 | var arr1 = ['a','b','c']; var arr2 = []; for (var i=0; i < arr1.length; i++) { arr2[i] = arr1[i]; } |
您可以在Google上搜索关于不可变原语值和可变对象引用的更多信息。
快速examples: P / < >
1 2 3 4 5 6 7 | 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' ] |
P / < >
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 ] ] |
P / < >
1 2 3 4 5 6 7 8 | 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 ] ] |
P / < >
使用jquery深度复制可以进行如下操作:
1 | var arr2 = $.extend(true, [], arr1); |
这里是一些更多的方式来拷贝: P / < >
1 2 3 4 5 6 | 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 ); |
P / < >
如果数组包含基元数据类型的元素(如int、char或string等),则可以使用这些方法中的一种,该方法返回原始数组的副本(如.slice()或.map()或spread运算符,这要归功于es6)。
1 | new_array = old_array.slice() |
或
1 | new_array = old_array.map((elem) => elem) |
或
1 | const new_array = new Array(...old_array); |
但是,如果数组包含对象(或数组)或更多嵌套对象等复杂元素,则必须确保将所有元素从顶层复制到最后一层,否则将使用内部对象的引用,这意味着更改新数组中对象元素的值仍会影响旧数组γ阵列。您可以在每个级别将此复制方法称为进行深度复制旧阵列的。
对于深度复制,您可以根据数据类型在每个级别对原始数据类型使用上面提到的方法,或者可以使用这种昂贵的方法(下面提到)进行深度复制,而不需要做很多工作。
1 | var new_array = JSON.parse(JSON.stringify(old_array)); |
根据您的需求,有很多其他方法可以使用。我只提到了其中的一些内容,用于大致了解当我们尝试按值将数组复制到另一个数组时会发生什么。
有新推出的
1 2 | var arr1 = [1, 2, 3]; console.log(Array.from(arr1)); // Logs: [1, 2, 3] |
参考文献:这里
或
1 2 3 4 5 6 7 | function identity(param) { return param; } var arr1 = [1, 2, 3], clone = arr1.map(identity); |
参考文献:这里
你可以用es6与传播opeartor,其simpler。 P / < >
1 | arr2 = [...arr1]; |
有limitations ..check价差文档语法mozilla @ P / < >
你可以这样做,在以下的方式:
1 | let a = [1,2,3]; |
现在,您可以执行以下任一操作来创建数组的副本。
1 | let b = Array.from(a); |
或
1 | let b = [...a]; |
或
1 | let b = new Array(...a); |
或
1 | let b = a.slice(); |
或
1 | let b = a.map(e => e); |
现在,如果我换了A,
1 | a.push(5); |
那么,a是[1,2,3,5]但是b仍然是[1,2,3],因为它有不同的引用。
但我认为,在上述所有方法中,array.from更好,主要是为了复制一个数组。
这里有一个变种:
1 2 3 4 5 6 7 8 9 | var arr1=['a', 'b', 'c']; var arr2=eval(arr1.toSource()); arr2.push('d'); console.log('arr1: '+arr1+' arr2: '+arr2); /* * arr1: a,b,c * arr2: a,b,c,d */ |
下面是如何对可变深度的基元数组进行此操作:
1 2 3 4 5 6 7 8 | // 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); |
https://jsbin.com/xemazog/edit?控制台
基本值总是通过其值传递(复制)。复合值通过引用传递。
那么我们如何复制这个ARR?
1 | let arr = [1,2,3,4,5]; |
在ES6中复制数组
1 | let arrCopy = [...arr]; |
在ES5中复制n数组
1 2 | let arrCopy = arr.slice(); let arrCopy = [].concat(arr); |
为什么'let arrcopy=arr'不按值传递?
在复合值(如对象/数组)上将一个变量传递给另一个变量的行为不同。在copand值上使用asign运算符,我们将引用传递给对象。这就是删除/添加arr元素时两个数组的值都在变化的原因。
Exceptios:1 | arrCopy[1] = 'adding new value this way will unreference'; |
为变量指定新值时,将更改引用本身,而不会影响原始对象/数组。
多读
我个人更喜欢这种方式json.parse(json.stringify(originalObject));
为es6阵列包含的物体 P / < >
1 2 3 | cloneArray(arr) { return arr.map(x => ({ ...x })); } |
只是写作:
1 | arr2 = arr1.concat(); |
您正在使用第一个数组的副本生成新的数组。请注意,这是将元素推入数组的一种功能性方法。
如果您的代码基于ES6,则也可以使用排列运算符:
1 | arr2 = [...arr1]; |