在 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
相同的数组,而不是一个新的独立数组。如何将数组复制以获得两个独立的数组?let oldArray = [1, 2, 3, 4, 5];
let newArray = oldArray.slice();
console.log({newArray});
基本上,slice()
操作会克隆整个数组并返回一个指向新数组的引用。
对于引用、字符串和数字(而非实际对象),slice()
将对象引用复制到新数组中。原始数组和新数组都指向同一对象。如果引用的对象发生更改,则更改对新数组和原始数组都可见。
字符串和数字等基本数据类型是不可变的,因此无法更改字符串或数字。
.slice()
和.splice()
,后者会给你一个空数组,两者有很大的区别。 - crazypeter在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 () {} ];
根据数组元素的类型,我们可以使用不同的技术来进行深拷贝。
https://www.measurethat.net/Benchmarks/Show/17502/0/deep-copy-comparison
仅包含字面值的数组(类型1)
可以使用 [ ...myArray ]
、myArray.splice(0)
、myArray.slice()
和 myArray.concat()
技术对仅包含字面值(布尔值、数字和字符串)的数组进行深拷贝;其中,在 Chrome 中,slice()
的性能最高,在 Firefox 中, spread ...
的性能最高。
包含字面值(类型1)和字面结构的数组(类型2)
可以使用 JSON.parse(JSON.stringify(myArray))
技术进行字面值(布尔值、数字、字符串)和字面结构(数组、对象)的深拷贝,但不能处理原型对象。
所有类型的数组(类型1、类型2、类型3)
function copy(aObject) {
// Prevent undefined objects
// if (!aObject) return aObject;
let bObject = Array.isArray(aObject) ? [] : {};
let value;
for (const key in aObject) {
// Prevent self-references to parent object
// if (Object.is(aObject[key], aObject)) continue;
value = aObject[key];
bObject[key] = (typeof value === "object") ? copy(value) : value;
}
return bObject;
}
var arr1 = ['a','b','c'];
var arr2 = arr1;
我意识到arr2指的是与arr1相同的数组,而不是一个新的、独立的数组。我该如何复制这个数组以获得两个独立的数组?
因为arr1
是由文字值(布尔值、数字或字符串)组成的数组,所以您可以使用任何上面讨论的深拷贝技术,其中slice()
和展开运算符...
具有最高的性能。
arr2 = arr1.slice();
arr2 = [...arr1];
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = copy(arr1); // Custom function needed, and provided above
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = jQuery.extend(true, [], arr1); // jQuery.js needed
arr1
的原始文本值。这种情况非常罕见。
使用splice
会抹掉arr1
,所以这根本不是复制。
如果数组中的任何值是函数或具有原型(如日期),则使用JSON
将失败。 - Dancrumb[0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]]
和其他任何数组之间没有区别。 - wizzwizz4x.clone()
有什么问题吗? - Jonny不需要 jQuery... 工作示例
var arr2 = arr1.slice()
这将从起始位置0
复制数组到其末尾。
重要的是要注意,对于基本类型(字符串,数字等),它将按预期工作,并且还要说明引用类型的预期行为...
如果您有一个引用类型的数组,比如Object
类型。该数组将会被复制,但两个数组都包含指向相同Object
的引用。因此,在这种情况下,虽然数组实际上是被复制了的,但似乎像是通过引用复制了数组。
var arr2 = JSON.stringify(arr1);
arr2 = JSON.parse(arr2);
- pradeep1991singh在尝试了多种方法之后,我是这样做的:
var newArray = JSON.parse(JSON.stringify(orgArray));
这将创建一个新的深层副本,与第一个副本无关(不是浅层副本)。
另外,显然这不会克隆事件和函数,但好消息是你只需要一行代码就可以完成,并且可以用于任何类型的对象(数组、字符串、数字、对象等)。
Date
这样有原型的任何对象都会失败。此外, undefined
被转换为null
。 - Dancrumb使用 concat
作为 slice
的替代方案,它有两种用法。第一种用法可能更易读,因为预期的行为非常明确:
var array2 = [].concat(array1);
第二种方法是:var array2 = array1.concat();
Cohen(在评论中)指出,后一种方法具有更好的性能。
这种方法的原理是concat
方法创建一个新的数组,该数组由调用它的对象中的元素,以及传递给它作为参数的任何数组中的元素组成。因此,当没有传递参数时,它只会复制该数组。
同样在评论中,Lee Penkman 指出,如果有可能array1
是undefined
,则可以按以下方式返回一个空数组:
var array2 = [].concat(array1 || []);
或者,对于第二种方法:
var array2 = (array1 || []).concat();
请注意您也可以使用 slice
来实现这个功能:var array2 = (array1 || []).slice();
.
[].concat(array1)
将返回[array1]
,例如如果它是未定义的,则会得到[undefined]
。有时我会使用var array2 = [].concat(array1 || []);
。 - lee penkman我个人认为Array.from是一种更易读的解决方案。顺便提一下,要注意它的浏览器支持情况。
// clone
let x = [1, 2, 3];
let y = Array.from(x);
console.log({y});
// deep clone
let clone = arr => Array.from(arr, item => Array.isArray(item) ? clone(item) : item);
x = [1, [], [[]]];
y = clone(x);
console.log({y});
.slice()
解决方案完全不直观。感谢您提供这个。 - Banago在处理数字或字符串等简单数据类型时,一些提到的方法可以很好地工作,但是当数组包含其他对象时,这些方法就会失败。当我们尝试将任何对象从一个数组传递到另一个数组时,它会作为引用而不是对象传递。
请将以下代码添加到您的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];
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