复制数组的值

2123

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

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

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

4
在Chrome 53和Firefox 48中,slicesplice操作的性能表现很好,而新的扩展运算符和Array.from的实现则比较慢。可参考perfjs.fnfo - Pencroff
https://jsperf.com/flat-array-copy - Walle Cyril
对于包含原始字符串的数组,您可以使用 var arr2 = arr1.splice(); 进行深度复制,但是如果您的数组元素包含文字结构(即 []{})或原型对象(即 function () {}new 等),则此技术将无法工作。请参见下面的答案以获取更多解决方案。 - tim-montague
41
2017年了,你可以考虑使用ES6的特性:let arr2 = [...arr1];。详见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator。 - Hinrich
2
当你声明 a = b; 时,实际上是告诉程序在随机访问内存中指向同一个符号链接。当更改此符号链接处的值时,会影响到 ab... 因此,如果你使用扩展运算符 a= [...b];,程序将创建一个额外的符号链接到随机访问内存中的不同位置,然后你可以独立地操作 ab - 71GA
显示剩余3条评论
40个回答

2

快速示例:

  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
以下是几种复制的方法:

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


1

structuredClone是一种新的深度克隆方式。

structuredClone(value)
structuredClone(value, { transfer })

transfer是一个包含了需要移动而不是复制到返回对象中的可传递对象的数组。

你可能会发现它的算法非常有趣。


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

虽然这不是一个坏主意,但我最好使用JSON stringify/parse而不是eval,另外进行另一个jsPerf比较会很好,还要注意toSource不是标准的,在Chrome中无法工作。 - dmi3y

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

https://jsbin.com/xemazog/edit?js,console


1

只是写着:

arr2 = arr1.concat();

你正在生成一个新数组,该数组是第一个数组的副本。请注意,这是将元素推入数组的函数方法。
如果你的代码基于ES6,你也可以使用扩展运算符:
arr2 = [...arr1];

1
您可以按以下方式完成此操作:
arr2 = arr1.map(x => Object.assign({}, x));

1

近期新增了 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 因为提到了 Array.from,该方法现在在除了 Internet Explorer 以外的所有主流浏览器上都得到支持(来源)。 - mgthomas99
请注意,Array.from 会改变数据,不提供深度复制/深度克隆。 - Sudhansu Choudhary

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 会受到影响。


-1

经过深入研究,我发现一个干净的方法是:

  const arr1 = [['item 1-1', 'item 1-2'], ['item 2-1', 'item 2-2'], ['item 3-1', 'item 3-2']];

  /**
   * Using Spread operator, it will create a new array with no reference to the first level.
   * 
   * Since, the items are not primitive, they get their own references. It means that any change on them,
   * it will be still reflected on the original object (aka arr1).
   */
  const arr2 = [...arr1];

  /**
   * Using Array.prototype.map() in conjunction Array.prototype.slice() will ensure:
   * - The first level is not a reference to the original array.
   * - In the second level, the items are forced (via slice()) to be created as new ones, so there is not reference to the original items
   */
  const arr3 = arr1.map(item => item.slice());

你需要理解你想要操作的数组的复杂性,然后应用最佳解决方案(即在引用数组内引用项目)


只有当数组中的元素也都是数组时,这才有效。 - Brandon Hunter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接