按值复制数组

IT技术 javascript arrays
2020-12-11 16:13:23

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

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

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

6个回答

用这个:

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

let newArray = oldArray.slice();

console.log({newArray});

基本上,该slice()操作会克隆数组并返回对新数组的引用。

另请注意:

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

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

@GáborImre 你会为了可读性而添加一个完整的库吗?真的吗?如果我担心可读性,我只会添加评论。请参阅: var newArray = oldArray.slice(); //将oldArray克隆为newArray
2021-02-07 16:13:23
@GáborImre 我明白了,当然。但是在我看来,通过包含整个库来回答特定的工程问题并没有帮助,这是设计膨胀。我看到开发人员经常这样做,然后你最终得到了一个包含整个框架的项目,以取代必须编写单个函数。不过,只是我的 MO。
2021-02-16 16:13:23
经验教训:不要.slice()混淆.splice(),它会为您提供一个空数组。巨大差距。
2021-02-16 16:13:23
关于性能,以下 jsPerf 测试实际上表明 var arr2 = arr1.slice() 与 var arr2 = arr1.concat(); 一样快;JSPerf:jsperf.com/copy-array-slice-vs-concat/5jsperf.com/copy-simple-arrayjsperf.com/array-copy/5的结果让我很惊讶,以至于我想知道测试代码是否有效。
2021-02-22 16:13:23
尽管这已经收到了大量的赞成票,但它值得另一个,因为它正确地描述了 JS 中的引用,不幸的是,这种情况很少见。
2021-03-04 16:13:23

在 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 深度复制技术

  • 文字值(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()具有比http://jsperf.com/js-deep-copy/15JSON.parse(JSON.stringify(myArray))技术更高的性能 对于那些回避第三方库(如 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
使用 splice 是部分解决方案。它会在比 JSON 多得多的情况下失败。当 Splice 移动值时,它会创建一个字符串和数字的深层副本——从来没有说过它返回一个副本。
2021-02-23 16:13:23
为什么拼接(0)?不应该是 slice() 吗?我认为它不应该修改原始数组,拼接会这样做。@詹姆斯蒙塔尼
2021-02-25 16:13:23
实际上,只有一种类型的数组:“某物”数组。[0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]]和任何其他数组之间没有区别
2021-02-27 16:13:23
其中许多方法效果不佳。使用赋值运算符意味着您必须重新分配 的原始文字值arr1这种情况非常罕见。使用spliceObliterates arr1,所以这根本不是副本。JSON如果数组中的任何值是函数或具有原型(例如 a Date),使用将失败
2021-03-09 16:13:23
splice 将创建指向原始数组(浅拷贝)中元素的指针。splice(0) 将为数组中的数字或字符串元素分配新的内存(深拷贝),并为所有其他元素类型(浅拷贝)创建指针。通过将零起始值传递给 splice 函数方法,它不会拼接来自原始数组的任何元素,因此它不会修改它。
2021-03-09 16:13:23

您可以使用数组扩展...来复制数组。

const itemsCopy = [...items];

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

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

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

有关点差的更多信息

这不适用于深拷贝。在 JavaScript 中深度克隆数组
2021-03-06 16:13:23

不需要 jQuery... 工作示例

var arr2 = arr1.slice()

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

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

如果你有一个引用类型的数组,比如 type Object该数组被复制,但两个数组都将包含对相同Object's 的引用因此,在这种情况下,即使实际复制了数组,该数组似乎也是通过引用复制的。

尝试这个; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);
2021-02-08 16:13:23
这个答案和接受的答案有什么区别?
2021-02-11 16:13:23
在给定示例的控制台中出现错误“TypeError: window.addEvent is not a function”
2021-02-19 16:13:23
@IsaacPak 在此之前2 分钟已回答
2021-03-01 16:13:23
不,这不会是深拷贝。
2021-03-05 16:13:23

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

var array2 = [].concat(array1);

第二种方法是:

var array2 = array1.concat();

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

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

Lee Penkman 也在评论中指出,如果有机会array1is undefined,您可以返回一个空数组,如下所示:

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

或者,对于第二种方法:

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

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

其实你也可以这样做: var array2 = array1.concat(); 它的性能要快得多。(JSPerf:jsperf.com/copy-simple-arrayjsperf.com/copy-array-slice-vs-concat/5
2021-02-08 16:13:23
值得注意的是,如果 array1 不是数组,则[].concat(array1)返回,[array1]例如,如果它未定义,您将得到[undefined]. 我有时会var array2 = [].concat(array1 || []);
2021-02-19 16:13:23