按值复制数组

Dan*_*Dan 1638 javascript arrays

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

var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d');  //Now, arr1 = ['a','b','c','d']
Run Code Online (Sandbox Code Playgroud)

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

Sak*_*ket 2597

用这个:

var newArray = oldArray.slice();
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 虽然这已经收到了大量的赞成票,但它应该得到另一个,因为它恰当地描述了JS中的引用,遗憾的是,它很少见. (89认同)
  • @GáborImre你为了便于阅读而添加整个图书馆?真?如果我担心可读性,我会添加评论.请参阅:var newArray = oldArray.slice(); //将oldArray克隆到newArray (32认同)
  • @GáborImre我明白了.但是在我看来通过包含整个库来回答特定的工程问题是没有用的,这是设计膨胀.我看到开发人员做了很多,然后你最终得到了一个包含整个框架的项目,以替换必须编写单个函数.不过只是我的MO. (12认同)
  • 关于性能,以下jsPerf测试实际上表明var arr2 = arr1.slice()与var arr2 = arr1.concat()一样快; JSPerf:http://jsperf.com/copy-array-slice-vs-concat/5和http://jsperf.com/copy-simple-array.http://jsperf.com/array-copy/5的结果令我感到惊讶,我想知道测试代码是否有效. (9认同)
  • 获得的经验:不要将`.slice()`与`.splice()`混淆,它会为你提供一个空数组.很大的区别. (4认同)
  • @Growler绝对_ **不是** _深层复制!它将保留对原始对象的所有引用。 (3认同)

tfm*_*gue 491

在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 () {}"
Run Code Online (Sandbox Code Playgroud)

从这些元素中我们可以创建三种类型的数组.

// 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 () {}];
Run Code Online (Sandbox Code Playgroud)

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

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

按元素类型的Javascript深层复制技术

  • 文字值(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)技术可用于深层复制所有数组类型.像UnderscoreLo-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;
}
Run Code Online (Sandbox Code Playgroud)

所以回答这个问题......

var arr1 = ['a','b','c'];
var arr2 = arr1;
Run Code Online (Sandbox Code Playgroud)

我意识到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
Run Code Online (Sandbox Code Playgroud)

  • 其中许多方法效果不佳。使用赋值运算符意味着您必须重新分配 `arr1` 的原始文字值。这种情况非常罕见。使用 `splice` 会删除 `arr1`,因此它根本不是副本。如果数组中的任何值是函数或具有原型(例如“日期”),则使用“JSON”将失败。 (2认同)
  • splice将创建指向原始数组中元素的指针(浅拷贝).splice(0)将为数组中的元素(数字或字符串)分配新内存(深层副本),并为所有其他元素类型(浅层副本)创建指针.通过将起始值0传递给splice函数方法,它不会拼接原始数组中的任何元素,因此不会对其进行修改. (2认同)
  • 实际上,只有一种类型的数组:“某物”数组。`[0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12] 没有区别,13]]]` 和任何其他数组。 (2认同)

Luk*_*mur 182

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

const itemsCopy = [...items];

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

var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
Run Code Online (Sandbox Code Playgroud)

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

有关点差的更多信息

  • 这不适用于深层复制。[在 JavaScript 中深度克隆数组](https://dev.to/samanthaming/how-to-deep-clone-an-array-in-javascript-3cig)。 (4认同)

jon*_*ohn 152

不需要jQuery ...... 工作示例

var arr2 = arr1.slice()
Run Code Online (Sandbox Code Playgroud)

这将从阵列的起始位置0到末尾复制阵列.

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

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

  • 不,这不是一个深刻的副本. (11认同)
  • 这个答案和接受的答案有什么区别? (2认同)

Nin*_*non 71

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

var array2 = [].concat(array1);
Run Code Online (Sandbox Code Playgroud)

第二种方法是:

var array2 = array1.concat();
Run Code Online (Sandbox Code Playgroud)

科恩(在评论中)指出后一种方法有更好的表现.

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

李Penkman,也是在评论中指出,如果有一个机会array1undefined,你可以按照如下返回一个空数组:

var array2 = [].concat(array1 || []);
Run Code Online (Sandbox Code Playgroud)

或者,对于第二种方法:

var array2 = (array1 || []).concat();
Run Code Online (Sandbox Code Playgroud)

请注意,您也可以使用slice:var array2 = (array1 || []).slice();.

  • 实际上你也可以这样做:var array2 = array1.concat(); 它的性能要快得多.(JSPerf:http://jsperf.com/copy-simple-array和http://jsperf.com/copy-array-slice-vs-concat/5 (31认同)
  • 值得注意的是,如果array1不是数组,则`[] .concat(array1)`返回`[array1]`,例如,如果它未定义,你将得到`[undefined]`.我有时会做`var array2 = [] .concat(array1 || []);` (4认同)

Cht*_*lek 56

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

var newArray = JSON.parse(JSON.stringify(orgArray));
Run Code Online (Sandbox Code Playgroud)

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

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

  • 没有人勇敢地评论序列化文本然后解析回对象的CPU和内存的总体低效率吗? (6认同)
  • 这是最好的一个.很久以前我使用相同的方法,并认为在旧学校的递归循环中没有更多的意义 (3认同)
  • 这个解决方案是唯一有效的解决方案.使用slice()实际上是一个假的解决方案. (3认同)
  • 请注意,此选项不能很好地处理类似图的结构:存在循环时崩溃,并且不保留共享引用。 (2认同)
  • 这对于像“Date”这样的东西,或者实际上,任何有原型的东西也失败了。此外,`undefined`s 被转换为 `null`s。 (2认同)

小智 19

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

在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;
};
Run Code Online (Sandbox Code Playgroud)

简单地使用

var arr1 = ['val_1','val_2','val_3'];
var arr2 = arr1.clone()
Run Code Online (Sandbox Code Playgroud)

它会工作.

  • @Jason但是对象仍然指向同一个对象,所以改变一个对象会改变另一个对象.http://jsfiddle.net/k525g/1/ (7认同)
  • 当我将此代码添加到我的页面'Uncaught RangeError:超出最大调用堆栈大小'时,我收到此错误 (2认同)

Boo*_*jaa 16

从ES2015开始,

var arr2 = [...arr1];
Run Code Online (Sandbox Code Playgroud)


Lew*_*wis 15

我个人认为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);
Run Code Online (Sandbox Code Playgroud)

  • 是的,这是非常可读的。`.slice()` 解决方案完全不直观。谢谢你。 (2认同)

ulo*_*lou 14

重要!

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

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

let clonedArray = [...array]

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

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


对于lodash用户:

let clonedArray = _.clone(array) 文件

let clonedArray = _.cloneDeep(array) 文件


Wal*_*anG 11

如果您在ECMAScript 6的环境中,使用Spread Operator可以这样做:

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

console.log(arr1)
console.log(arr2)
Run Code Online (Sandbox Code Playgroud)
<script src="http://www.wzvang.com/snippet/ignore_this_file.js"></script>
Run Code Online (Sandbox Code Playgroud)


Dev*_*vWL 11

原始值总是通过其值传递(复制)。然而,复合值是通过引用传递的。

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

let arr = [1,2,3,4,5];
Run Code Online (Sandbox Code Playgroud)

在 ES6 中复制数组

let arrCopy = [...arr]; 
Run Code Online (Sandbox Code Playgroud)

在 ES5 中复制 n 数组

let arrCopy = arr.slice(); 
let arrCopy = [].concat(arr);
Run Code Online (Sandbox Code Playgroud)

为什么`let arrCopy = arr` 不按值传递?

将一个变量传递给另一个对象/数组等复合值的行为会有所不同。在 copand 值上使用 asign 运算符,我们将引用传递给一个对象。这就是为什么在删除/添加 arr 元素时两个数组的值都会发生变化的原因。

例外:

arrCopy[1] = 'adding new value this way will unreference';
Run Code Online (Sandbox Code Playgroud)

当您为变量分配一个新值时,您正在更改引用本身,它不会影响原始对象/数组。

阅读更多


小智 9

添加到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]);
Run Code Online (Sandbox Code Playgroud)

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


Sup*_*ool 8

我发现这个方法比较简单:

let arr = [1,2,3,4,5];
let newArr = [...arr];
console.log(newArr);
Run Code Online (Sandbox Code Playgroud)


Nit*_*jan 7

let a = [1,2,3];
Run Code Online (Sandbox Code Playgroud)

现在,您可以执行以下任一操作来制作数组的副本。

let b = Array.from(a); 
Run Code Online (Sandbox Code Playgroud)

或者

let b = [...a];
Run Code Online (Sandbox Code Playgroud)

或者

let b = new Array(...a); 
Run Code Online (Sandbox Code Playgroud)

或者

let b = a.slice(); 
Run Code Online (Sandbox Code Playgroud)

或者

let b = a.map(e => e);
Run Code Online (Sandbox Code Playgroud)

现在,如果我改变一个,

a.push(5); 
Run Code Online (Sandbox Code Playgroud)

然后,a 是 [1,2,3,5] 但 b 仍然是 [1,2,3] 因为它有不同的参考。

但我认为,在上述所有方法中 Array.from 更好,主要用于复制数组。


Die*_*rdk 7

我个人更喜欢这种方式:

JSON.parse(JSON.stringify( originalObject ));
Run Code Online (Sandbox Code Playgroud)


Nim*_*dar 7

当有很多答案时,您必须使用最佳实践来解决此问题。

\n

我建议您使用数组扩展 \xe2\x80\xa6 来复制数组。

\n

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

\n

var arr2 = [\xe2\x80\xa6arr1];

\n


Ali*_*eza 6

正如我们所知,在Javascript 数组对象是通过引用,但我们可以做什么方式复制数组而不更改原来的数组?

以下是几种方法:

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

var arr = [1, 2, 3, 4, 5];
Run Code Online (Sandbox Code Playgroud)

1)在函数中循环遍历数组并返回一个新数组,如下所示:

 function newArr(arr) {
      var i=0, res = [];
      while(i<arr.length){
       res.push(arr[i]);
        i++;
       }
   return res;
 }
Run Code Online (Sandbox Code Playgroud)

2)使用切片方法,切片用于切片数组的一部分,它将切片数组的某些部分而不接触原始数据,在切片中,如果不指定数组的开始和结束,它将切片整个数组和基本上是数组的完整副本,所以我们可以很容易地说:

var arr2 = arr.slice(); // make a copy of the original array
Run Code Online (Sandbox Code Playgroud)

3)也是联系方法,这是为了合并两个数组,但是我们可以只指定一个数组,然后这基本上是新联系数组中的值的副本:

var arr2 = arr.concat();
Run Code Online (Sandbox Code Playgroud)

4)也是字符串化和解析方法,不推荐,但可以是一种复制数组和对象的简单方法:

var arr2 = JSON.parse(JSON.stringify(arr));
Run Code Online (Sandbox Code Playgroud)

5)Array.from方法,这个没有被广泛支持,在使用之前检查不同浏览器中的支持:

const arr2 = Array.from(arr);
Run Code Online (Sandbox Code Playgroud)

6)ECMA6的方式,也没有完全支持,但如果你想要转换,babelJs可以帮助你:

const arr2 = [...arr];
Run Code Online (Sandbox Code Playgroud)


Dra*_*tor 5

丹,不需要使用花哨的技巧。您需要做的就是通过执行此操作制作 arr1 的副本。

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

var arr2 = new Array(arr1);

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

console.log('arr1:');
console.log(arr1);

console.log('arr2:');
console.log(arr2);

// Following did the trick:
var arr3 = [...arr1];
arr3.push('d');  // Now, arr3 = ['a','b','c','d'];

console.log('arr3:');
console.log(arr3);
Run Code Online (Sandbox Code Playgroud)

现在arr1arr2是存储在不同堆栈中的两个不同的数组变量。 在 jsfiddle 上检查一下

  • 这不会复制数组。它创建了一个数组,其中一个元素引用了原始元素(即`var arr2 = [arr1];`)。 (3认同)

Blu*_*Pie 5

您可以将 ES6 与扩展 Opeartor 一起使用,它更简单。

arr2 = [...arr1];
Run Code Online (Sandbox Code Playgroud)

有限制..检查文档扩展语法@ mozilla