719

为了在 JavaScript 中复制一个数组:以下哪个使用起来更快?

Slice方法

var dup_array = original_array.slice();

For环形

for(var i = 0, len = original_array.length; i < len; ++i)
   dup_array[i] = original_array[i];

我知道这两种方式都只做一个浅拷贝:如果original_array包含对对象的引用,则不会克隆对象,但只会复制引用,因此两个数组都将引用相同的对象。但这不是这个问题的重点。

我只问速度。

4

24 回答 24

866

克隆数组至少有6种(!)方法:

  • 环形
  • 数组.from()
  • 连接
  • 扩展运算符(最快)
  • 地图A.map(function(e){return e;});

有一个 huuuge BENCHMARKS 线程,提供以下信息:

  • 对于眨眼浏览器slice()是最快的方法,concat()速度稍慢,速度慢while loop2.4 倍。

  • 对于其他浏览器while loop是最快的方法,因为这些浏览器没有对slice和的内部优化concat

2016 年 7 月仍然如此。

下面是一些简单的脚本,您可以将它们复制粘贴到浏览器的控制台中并运行几次以查看图片。它们输出毫秒,越低越好。

while 循环

n = 1000*1000;
start = + new Date();
a = Array(n); 
b = Array(n); 
i = a.length;
while(i--) b[i] = a[i];
console.log(new Date() - start);

n = 1000*1000;
start = + new Date();
a = Array(n); 
b = a.slice();
console.log(new Date() - start);

请注意,这些方法将克隆 Array 对象本身,但数组内容是通过引用复制的,而不是深度克隆。

origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true
于 2013-12-12T15:42:47.020 回答
293

技术上slice 最快的方法。但是0,如果您添加开始索引,它会更快。

myArray.slice(0);

myArray.slice();

https://web.archive.org/web/20170824010701/https://jsperf.com/cloning-arrays/3

于 2014-02-02T18:11:04.340 回答
171

es6方式呢?

arr2 = [...arr1];
于 2015-07-06T16:34:08.017 回答
53

深度克隆数组或对象的最简单方法:

var dup_array = JSON.parse(JSON.stringify(original_array))
于 2014-05-05T20:23:29.933 回答
37
var cloned_array = [].concat(target_array);
于 2016-10-05T12:00:15.707 回答
31

克隆数组的最快方法

我制作了这个非常简单的实用函数来测试克隆数组所需的时间。它不是 100% 可靠的,但它可以让您大致了解克隆现有阵列需要多长时间:

function clone(fn) {
  const arr = [...Array(1000000)];
  console.time('timer');
  fn(arr);
  console.timeEnd('timer');
}

并测试了不同的方法:

1)   5.79ms -> clone(arr => Object.values(arr));
2)   7.23ms -> clone(arr => [].concat(arr));
3)   9.13ms -> clone(arr => arr.slice());
4)  24.04ms -> clone(arr => { const a = []; for (let val of arr) { a.push(val); } return a; });
5)  30.02ms -> clone(arr => [...arr]);
6)  39.72ms -> clone(arr => JSON.parse(JSON.stringify(arr)));
7)  99.80ms -> clone(arr => arr.map(i => i));
8) 259.29ms -> clone(arr => Object.assign([], arr));
9) Maximum call stack size exceeded -> clone(arr => Array.of(...arr));

更新

  1. 测试是在 2018 年进行的,所以今天你很可能会在当前浏览器上得到不同的结果。
  2. 在所有这些中,深度克隆数组的唯一方法是使用JSON.parse(JSON.stringify(arr)).

    也就是说,如果您的数组可能包含函数,请不要使用上述内容,因为它将返回null.
    感谢@GilEpshtain 提供此更新

我在 Chrome 和 Firefox 中运行 @mesqueeb 的基准测试。slice()似乎仍然是一个相当不错的选择。

铬 95.0: Chrome 阵列复制基准

火狐 94.0: Firefox 阵列复制基准

于 2018-10-22T12:50:54.063 回答
26

我整理了一个快速演示:http: //jsbin.com/agugo3/edit

我在 Internet Explorer 8 上的结果是 156、782 和 750,这表明slice在这种情况下要快得多。

于 2010-10-20T14:06:07.003 回答
22

a.map(e => e)是这项工作的另一种选择。到目前为止,在 Firefox.map()中速度非常快(几乎与.slice(0)Firefox 一样快),但在 Chrome 中不是。

另一方面,如果一个数组是多维的,因为数组是对象,而对象是引用类型,所以 slice 或 concat 方法都不能治愈……所以克隆数组的一种正确方法是Array.prototype.clone()as跟随。

Array.prototype.clone = function(){
  return this.map(e => Array.isArray(e) ? e.clone() : e);
};

var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],
    brr = arr.clone();
brr[4][2][1] = "two";
console.log(JSON.stringify(arr));
console.log(JSON.stringify(brr));

于 2016-05-28T21:32:12.747 回答
8

看看:链接。这不是关于速度,而是关于舒适。此外,如您所见,您只能在原始类型上使用slice(0)

要制作数组的独立副本而不是对其引用的副本,您可以使用数组切片方法。

例子:

要制作数组的独立副本而不是对其引用的副本,您可以使用数组切片方法。

var oldArray = ["mip", "map", "mop"];
var newArray = oldArray.slice();

复制或克隆对象:

function cloneObject(source) {
    for (i in source) {
        if (typeof source[i] == 'source') {
            this[i] = new cloneObject(source[i]);
        }
        else{
            this[i] = source[i];
  }
    }
}

var obj1= {bla:'blabla',foo:'foofoo',etc:'etc'};
var obj2= new cloneObject(obj1);

来源:链接

于 2010-10-20T13:52:05.273 回答
8

ECMAScript 2015 使用Spread运算符的方式:

基本示例:

var copyOfOldArray = [...oldArray]
var twoArraysBecomeOne = [...firstArray, ..seccondArray]

在浏览器控制台中尝试:

var oldArray = [1, 2, 3]
var copyOfOldArray = [...oldArray]
console.log(oldArray)
console.log(copyOfOldArray)

var firstArray = [5, 6, 7]
var seccondArray = ["a", "b", "c"]
var twoArraysBecomOne = [...firstArray, ...seccondArray]
console.log(twoArraysBecomOne);

参考

于 2017-12-16T08:24:36.080 回答
8

克隆对象数组的最快方法是使用扩展运算符

var clonedArray=[...originalArray]

但是该克隆数组中的对象仍将指向旧的内存位置。因此更改 clonedArray 对象也将更改 orignalArray。所以

var clonedArray = originalArray.map(({...ele}) => {return ele})

这不仅会创建新数组,还会克隆对象。

于 2021-02-12T15:04:29.413 回答
7

正如@Dan 所说“这个答案很快就过时了。使用基准来检查实际情况”,jsperf 有一个具体的答案,它自己没有答案:while

var i = a.length;
while(i--) { b[i] = a[i]; }

有 960,589 ops/sec,亚军a.concat()为 578,129 ops/sec,占 60%。

这是最新的 Firefox (40) 64 位。


@aleclarson 创建了一个新的、更可靠的基准。

于 2015-08-27T15:33:14.483 回答
6

这取决于浏览器。如果您查看博客文章Array.prototype.slice 与手动创建数组,每个都有一个粗略的性能指南:

在此处输入图像描述

结果:

在此处输入图像描述

于 2010-10-20T13:55:22.603 回答
6

有一个更清洁的解决方案:

var srcArray = [1, 2, 3];
var clonedArray = srcArray.length === 1 ? [srcArray[0]] : Array.apply(this, srcArray);

长度检查是必需的,因为Array构造函数在仅使用一个参数调用时表现不同。

于 2014-04-28T13:31:57.677 回答
6

基准时间!

function log(data) {
  document.getElementById("log").textContent += data + "\n";
}

benchmark = (() => {
  time_function = function(ms, f, num) {
    var z = 0;
    var t = new Date().getTime();
    for (z = 0;
      ((new Date().getTime() - t) < ms); z++)
      f(num);
    return (z)
  }

  function clone1(arr) {
    return arr.slice(0);
  }

  function clone2(arr) {
    return [...arr]
  }

  function clone3(arr) {
    return [].concat(arr);
  }

  Array.prototype.clone = function() {
    return this.map(e => Array.isArray(e) ? e.clone() : e);
  };

  function clone4(arr) {
    return arr.clone();
  }


  function benchmark() {
    function compare(a, b) {
      if (a[1] > b[1]) {
        return -1;
      }
      if (a[1] < b[1]) {
        return 1;
      }
      return 0;
    }

    funcs = [clone1, clone2, clone3, clone4];
    results = [];
    funcs.forEach((ff) => {
      console.log("Benchmarking: " + ff.name);
      var s = time_function(2500, ff, Array(1024));
      results.push([ff, s]);
      console.log("Score: " + s);

    })
    return results.sort(compare);
  }
  return benchmark;
})()
log("Starting benchmark...\n");
res = benchmark();

console.log("Winner: " + res[0][0].name + " !!!");
count = 1;
res.forEach((r) => {
  log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
});
log("\nWinner code:\n");
log(res[0][0].toString());
<textarea rows="50" cols="80" style="font-size: 16; resize:none; border: none;" id="log"></textarea>

自您单击按钮后,基准测试将运行 10 秒。

我的结果:

铬(V8 引擎):

1. clone1 score: 100% *winner* (4110764)
2. clone3 score: 74.32% speed of winner. (3055225)
3. clone2 score: 30.75% speed of winner. (1264182)
4. clone4 score: 21.96% speed of winner. (902929)

火狐(蜘蛛猴引擎):

1. clone1 score: 100% *winner* (8448353)
2. clone3 score: 16.44% speed of winner. (1389241)
3. clone4 score: 5.69% speed of winner. (481162)
4. clone2 score: 2.27% speed of winner. (192433)

获胜者代码:

function clone1(arr) {
    return arr.slice(0);
}

赢家引擎:

蜘蛛猴 (Mozilla/Firefox)

于 2019-08-29T11:50:01.757 回答
6

请记住 .slice() 不适用于二维数组。你需要一个这样的函数:

function copy(array) {
  return array.map(function(arr) {
    return arr.slice();
  });
}
于 2016-01-21T13:02:10.700 回答
5

这取决于数组的长度。如果数组长度 <= 1,000,000,则sliceconcat方法所用的时间大致相同。但是,当您给出更大的范围时,该concat方法会获胜。

例如,试试这个代码:

var original_array = [];
for(var i = 0; i < 10000000; i ++) {
    original_array.push( Math.floor(Math.random() * 1000000 + 1));
}

function a1() {
    var dup = [];
    var start = Date.now();
    dup = original_array.slice();
    var end = Date.now();
    console.log('slice method takes ' + (end - start) + ' ms');
}

function a2() {
    var dup = [];
    var start = Date.now();
    dup = original_array.concat([]);
    var end = Date.now();
    console.log('concat method takes ' + (end - start) + ' ms');
}

function a3() {
    var dup = [];
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup.push(original_array[i]);
    }
    var end = Date.now();
    console.log('for loop with push method takes ' + (end - start) + ' ms');
}

function a4() {
    var dup = [];
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup[i] = original_array[i];
    }
    var end = Date.now();
    console.log('for loop with = method takes ' + (end - start) + ' ms');
}

function a5() {
    var dup = new Array(original_array.length)
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup.push(original_array[i]);
    }
    var end = Date.now();
    console.log('for loop with = method and array constructor takes ' + (end - start) + ' ms');
}

a1();
a2();
a3();
a4();
a5();

如果将 original_array 的长度设置为 1,000,000,则slice方法和concat方法所用的时间大致相同(3-4 毫秒,具体取决于随机数)。

如果将 original_array 的长度设置为 10,000,000,则该slice方法占用 60 毫秒以上,该concat方法占用 20 毫秒以上。

于 2015-11-18T07:35:52.867 回答
4

有几种方法可以克隆阵列。基本上,克隆分为两种方式:

  1. 浅拷贝
  2. 深拷贝

浅拷贝仅覆盖数组的第一级,其余的被引用。如果您想要数组中嵌套元素的真实副本,则需要深度克隆。

例子 :

const arr1 = [1,2,3,4,5,6,7]           
// Normal Array (shallow copy is enough)     
const arr2 = [1,2,3,[4],[[5]],6,7]          
// Nested Array  (Deep copy required) 


Approach 1 : Using (...)Spread Operator  (Shallow copy enough)
const newArray = [...arr1] // [1,2,3,4,5,6,7]

Approach 2 : Using Array builtIn Slice method (Deep copy)  
const newArray = arr1.slice()  // [1,2,3,4,5,6,7]

Approach 3 : Using Array builtIn Concat method (Deep a copy)
const newArray = [].concat(arr1)  // [1,2,3,4,5,6,7]

Approach 4 : Using JSON.stringify/parse. (Deep a copy & fastest)
const newArray = JSON.parse(JSON.stringify(arr2));)  // [1,2,3,[4],[[5]],6,7]

Approach 5: Using own recursive function or using loadash's __.cloneDeep method. (Deep copy)
于 2021-06-20T08:13:13.217 回答
4

在 ES6 中,您可以简单地使用Spread 语法

例子:

let arr = ['a', 'b', 'c'];
let arr2 = [...arr];

请注意,扩展运算符会生成一个全新的数组,因此修改一个不会影响另一个。

例子:

arr2.push('d') // becomes ['a', 'b', 'c', 'd']
console.log(arr) // while arr retains its values ['a', 'b', 'c']
于 2020-05-15T12:06:40.947 回答
3
        const arr = ['1', '2', '3'];

         // Old way
        const cloneArr = arr.slice();

        // ES6 way
        const cloneArrES6 = [...arr];

// But problem with 3rd approach is that if you are using muti-dimensional 
 // array, then only first level is copied

        const nums = [
              [1, 2], 
              [10],
         ];

        const cloneNums = [...nums];

// Let's change the first item in the first nested item in our cloned array.

        cloneNums[0][0] = '8';

        console.log(cloneNums);
           // [ [ '8', 2 ], [ 10 ], [ 300 ] ]

        // NOOooo, the original is also affected
        console.log(nums);
          // [ [ '8', 2 ], [ 10 ], [ 300 ] ]

因此,为了避免发生这些情况,请使用

        const arr = ['1', '2', '3'];

        const cloneArr = Array.from(arr);
于 2019-06-07T17:39:30.093 回答
3

一个简单的解决方案:

original = [1,2,3]
cloned = original.map(x=>x)
于 2017-08-16T00:24:16.463 回答
1

在 JavaScript 中按顺序复制数组的快速方法:

#1: array1copy = [...array1];

#2: array1copy = array1.slice(0);

#3: array1copy = array1.slice();

如果您的数组对象包含一些 JSON 不可序列化的内容(函数、Number.POSITIVE_INFINITY 等)更好地使用

array1copy = JSON.parse(JSON.stringify(array1))

于 2019-12-26T07:38:33.047 回答
1

您可以遵循此代码。不可变方式数组克隆。这是阵列克隆的完美方式


const array = [1, 2, 3, 4]

const newArray = [...array]
newArray.push(6)
console.log(array)
console.log(newArray)
于 2020-03-14T20:08:38.517 回答
1

如果你想要一个真正的 JS 中的克隆对象/数组,其中包含所有属性和子对象的克隆引用:

export function clone(arr) {
    return JSON.parse(JSON.stringify(arr))
}

所有其他操作都不会创建克隆,因为它们只是更改根元素的基地址,而不是包含对象的基地址。

除非您通过对象树递归遍历。

对于简单的副本,这些都可以。对于存储地址相关的操作,我建议(在大多数其他情况下,因为这很快!)将类型转换为字符串并返回一个完整的新对象。

于 2020-08-25T21:28:52.323 回答