it-swarm.cn

按值复制数组

将JavaScript中的数组复制到另一个数组时:

var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.Push('d');  //Now, arr1 = ['a','b','c','d']

我意识到arr2引用与arr1相同的数组,而不是一个新的独立数组。如何复制数组以获得两个独立的数组?

1501
Dan

用这个:

var newArray = oldArray.slice();

基本上, slice() operation克隆数组并返回对新数组的引用。另请注意:

对于引用,字符串和数字(​​而不是实际对象),slice()将对象引用复制到新数组中。原始数组和新数组都引用相同的对象。如果引用的对象发生更改,则更改对新的和原始数组都可见。

字符串和数字等原语是不可变的,因此不可能更改字符串或数字。 

2408
Saket

在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 () {}];

深层复制技术取决于三种阵列类型

根据数组中元素的类型,我们可以使用各种技术进行深度复制。

Javascript deep copy techniques by element types

  • 文字值数组(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)技术可用于深层复制所有数组类型。像 下划线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
435
tfmontague

您可以使用数组传播...来复制数组。

const itemsCopy = [...items];

此外,如果想要创建一个新数组,其中现有数组是其中的一部分:

var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];

数组传播现在 在所有主流浏览器中都支持 但如果您需要较旧的支持,请使用TypeScript或babel并编译为ES5。

有关点差的详细信息

169
Luke Femur

不需要jQuery ...工作示例

var arr2 = arr1.slice()

这会将数组从起始位置0复制到数组的末尾。

重要的是要注意它将按原始类型(字符串,数字等)的预期工作,并且还解释引用类型的预期行为......

如果你有一个引用类型数组,比如Object类型。数组被复制,但两个数组都将包含对同一Object的引用。所以在这种情况下,即使数组 实际上是复制的,看起来数组也是通过引用复制的。

150
jondavidjohn

sliceconcat 的替代方法,可以两种方式使用。第一个可能更具可读性,因为预期的行为非常明确:

var array2 = [].concat(array1);

第二种方法是:

var array2 = array1.concat();

科恩(在评论中)指出后一种方法 具有更好的性能

它的工作方式是concat方法创建一个新数组,该数组由调用它的对象中的元素组成,后跟作为参数传递给它的任何数组的元素。因此,当没有传递参数时,它只是复制数组。

Lee Penkman也在评论中指出,如果array1undefined的机会,你可以返回一个空数组,如下所示:

var array2 = [].concat(array1 || []);

或者,对于第二种方法:

var array2 = (array1 || []).concat();

请注意,您也可以使用slicevar array2 = (array1 || []).slice();执行此操作。

70
Ninjakannon

这是我在尝试了许多方法后的方法:

var newArray = JSON.parse(JSON.stringify(orgArray));

这将创建一个与第一个无关的新深拷贝(不是浅拷贝)。

此外,这显然不会克隆事件和函数,但你可以在一行中做到这一点,它可以用于任何类型的对象(数组,字符串,数字,对象......)

51
Chtiwi Malek

在处理数字或字符串等简单数据类型时,一些提到的方法效果很好,但是当数组包含其他对象时,这些方法会失败。当我们尝试将任何对象从一个数组传递到另一个数组时,它将作为引用而不是对象传递。

在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()

它会工作。

18
sarvesh singh

从ES2015开始,

var arr2 = [...arr1];
15
Boopathi Rajaa

我个人认为 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);
14
Lewis

重要!

这里的大多数答案适用于特殊情况

如果你不关心深度/嵌套对象和道具使用( ES6 ):

let clonedArray = [...array]

但如果你想做深度克隆,请改用:

let cloneArray = JSON.parse(JSON.stringify(array))


对于lodash用户:

let clonedArray = _.clone(array)文档

let clonedArray = _.cloneDeep(array)文档

13
ulou

如果你所在的环境 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>

11

添加到 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]);

同样的东西会转到对象数组,它们会被引用复制,你必须手动复制它们

9
A.Zaben

正如我们所知道的Javascript arrays objects 是引用,但是我们可以在不改变原始数组的情况下复制数组的方法是什么? 

以下是几种方法:

想象一下,我们在您的代码中有这个数组:

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];
6
Alireza

制作多维数组/对象的副本:

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的这项功能。

资料来源: 这里

4
ink

丹,不需要使用花哨的技巧。你需要做的就是通过这样做复制arr1。

var arr2 = new Array(arr1);

现在arr1arr2是存储在不同堆栈中的两个不同的数组变量。[。_____。] 在jsfiddle上检查这个

4
DragoRaptor

在我的特殊情况下,我需要确保阵列保持完整,这对我有用:

// 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]);
}
4
Brent Keller

当我们想要使用赋值运算符(=)复制数组时,它不会创建副本,而只是将指针/引用复制到数组中。例如:

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.parseJSON.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))(大量开销)。

源性能测试

3
Willem van der Veen

以下是几种复制方式:

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 );

2
ganesh phirke

快速示例:

  1. 如果数组中的元素是 原始类型 (字符串,数字等)

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' ]

  1. 如果数组中的元素是 对象文字,则另一个数组({},[])

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 ] ]

  1. 解决方案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 ] ]

2
SridharKritha

使用jQuery深层复制可以如下:

var arr2 = $.extend(true, [], arr1);
2
Alex Tsurika

您还可以使用ES6扩展运算符来复制Array

var arr=[2,3,4,5];
var copyArr=[...arr];
2
ankur kushwaha

如果您的数组包含 基本数据类型的元素 例如 int,char或string等 那么您可以使用其中一个返回原始数组副本的方法,例如。 slice()或.map()或spread运算符(感谢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。你可以在每个级别调用这种复制方法,因为它可以生成old_array的 DEEP COPY

对于深度复制,您可以根据数据的类型在每个级别使用上述方法处理原始数据类型,或者您可以使用这个 昂贵的方法(如下所述) 用于制作深层复制而不需要做太多工作。

var new_array = JSON.parse(JSON.stringify(old_array));

根据您的要求,您可以使用许多其他方法。我只提到了其中一些用于大致了解当我们尝试将数组复制到另一个 by value 时会发生什么。

2
Abhinav1602

如果要创建对象或数组的新副本,则必须显式复制对象的属性或数组的元素,例如:

var arr1 = ['a','b','c'];
var arr2 = [];

for (var i=0; i < arr1.length; i++) {
   arr2[i] = arr1[i];
}

您可以在Google上搜索有关不可变原始值和可变对象引用的更多信息。

2
Sotiris

我个人更喜欢这种方式JSON.parse(JSON.stringify(originalObject));

1
Diemauerdk

你可以使用带有传播Opeartor的ES6,它更简单。

arr2 = [...arr1];

有限制..检查文档 传播语法@ mozilla

1
BluePie

以下是如何为可变深度的基元数组做到这一点:

// 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?js,console

1
G.Denis

有新推出的Array.from,但不幸的是,截至本文撰写时,它仅支持最近的Firefox版本(32及更高版本)。它可以简单地使用如下:

var arr1 = [1, 2, 3];
console.log(Array.from(arr1)); // Logs: [1, 2, 3]

参考: 这里

或者Array.prototype.map可以与身份函数一起使用:

function identity(param)
{
    return param;
}

var arr1 = [1, 2, 3],
    clone = arr1.map(identity);

参考: 这里

1
Ashraf Sabry

您可以通过以下方式执行此操作:
arr2 = arr1.map(x => Object.assign({}, x));

1
Harunur Rashid
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更好,主要用于复制数组。

1
Nitesh Ranjan

这是一个变种:

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
 */
1
Zyox

只是写作:

arr2 = arr1.concat();

您正在生成一个带有第一个副本的新数组。请注意,这是将元素推入数组的功能方法。

如果您的代码基于ES6,您也可以使用扩展运算符:

arr2 = [...arr1];
0
alejoko

对于包含对象的ES6数组

cloneArray(arr) {
    return arr.map(x => ({ ...x }));
}
0
askilondz

原始值总是通过其值(复制)。复合值hovever通过引用传递。

那么我们如何复制这个arr? 

let arr = [1,2,3,4,5];

在ES6中复制一个数组 

let arrCopy = [...arr]; 

在ES5中复制n数组 

let arrCopy = arr.slice(); 
let arrCopy = [].concat(arr);

为什么`让arrCopy = arr`没有通过值?

将化合物值(例如对象/数组)上的一个变量传递给另一个变量表现不同。在copand值上使用asign运算符,我们传递对象的引用。这就是删除/添加arr元素时两个数组的值都在变化的原因。

Exceptios: 

arrCopy[1] = 'adding new value this way will unreference';

为变量分配新值时,您将更改引用本身,并且它不会影响原始对象/数组。 

阅读更多

0
DevWL