将 JavaScript 中的数组复制到另一个数组时:
var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d'); //Now, arr1 = ['a','b','c','d']
我意识到arr2
指向与arr1
相同的数组,而不是一个新的独立数组。如何复制数组以获取两个独立的数组?
用这个:
var newArray = oldArray.slice();
基本上, slice()
操作将克隆该数组并返回对新数组的引用。
对于引用,字符串和数字(而不是实际对象), slice()
对象引用复制到新数组中。原始数组和新数组都引用同一对象。如果引用的对象发生更改,则更改对新数组和原始数组均可见。
字符串和数字之类的基元是不可变的,因此无法更改字符串或数字。
在 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 () {}];
根据数组中元素的类型,我们可以使用各种技术进行深层复制。
文字值数组(类型 1)
[...myArray]
, myArray.splice(0)
, myArray.slice()
和myArray.concat()
技术只能用于深度复制具有文字值(布尔值,数字和字符串)的数组; Spread 运算符[...myArray]
的性能最佳( https://measurethat.net/Benchmarks/Show/4281/0/spread-array-performance-vs-slice-splice-concat )。
文字值(类型 1)和文字结构(类型 2)的数组
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 指向相同的数组,而不是一个新的独立数组。如何复制数组以获取两个独立的数组?
回答
因为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
不需要 jQuery ... 工作示例
var arr2 = arr1.slice()
这会将数组从起始位置0
复制到数组末尾。
重要的是要注意,它将对原始类型(字符串,数字等)按预期方式工作,并解释引用类型的预期行为...
如果您具有引用类型的数组,请说类型为Object
。该数组将被复制,但是两个数组都将包含对相同Object
的引用。因此,在这种情况下,即使实际复制了数组,也似乎是通过引用复制了该数组。
slice
的替代方法是concat
,可以有两种使用方式。由于预期的行为非常明确,因此第一个可能更易读:
var array2 = [].concat(array1);
第二种方法是:
var array2 = array1.concat();
科恩(在评论中)指出,后一种方法具有更好的性能 。
这种工作方式是concat
方法创建一个新数组,该数组由调用该对象的对象中的元素组成,然后是作为参数传递给它的任何数组的元素。因此,当不传递任何参数时,它仅复制数组。
Lee Penkman 在评论中也指出,如果有一个array1
undefined
的机会,您可以返回一个空数组,如下所示:
var array2 = [].concat(array1 || []);
或者,对于第二种方法:
var array2 = (array1 || []).concat();
注意,您也可以使用slice
来执行此操作: var array2 = (array1 || []).slice();
。
在尝试了多种方法之后,我就是这样做的:
var newArray = JSON.parse(JSON.stringify(orgArray));
这将创建一个与第一个副本无关的新的深副本(不是浅副本)。
同样,这显然不会克隆事件和函数,但是一件好事是您可以在一行中完成它,并且可以将其用于任何类型的对象(数组,字符串,数字,对象...)
当使用简单的数据类型(例如数字或字符串)时,上述方法中的某些方法可以很好地工作,但是当数组包含其他对象时,这些方法将失败。当我们尝试将任何对象从一个数组传递到另一个数组时,它将作为引用而不是对象传递。
在您的 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()
它会工作。
从 ES2015 开始,
var arr2 = [...arr1];
我个人认为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);