在JavaScript中复制数组的最快方法 - 切片与'for'循环

问题描述 投票:555回答:17

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

Slice method

var dup_array = original_array.slice();

For loop

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

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

我只询问速度。

javascript arrays duplicates copy slice
17个回答
719
投票

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

  • 切片
  • Array.from()
  • CONCAT
  • 传播运营商(最快)

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

  • 对于闪烁浏览器slice()是最快的方法,concat()有点慢,而while loop慢2.4倍。
  • 对于其他浏览器,while loop是最快的方法,因为这些浏览器没有sliceconcat的内部优化。

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

6
投票

看看:link。这不是速度,而是舒适。除此之外,您可以看到只能在基本类型上使用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);

资料来源:link


6
投票

正如@Dan所说:“这个答案变得过时了。使用benchmarks来检查实际情况”,jsperf有一个特定的答案,它没有自己的答案:while:

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

获得960,589次/秒,亚军a.concat()为578,129次/秒,即60%。

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


@aleclarson创造了一个新的,更可靠的基准。


6
投票

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

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

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


6
投票

记住.slice()不适用于二维数组。你需要这样的功能:

function copy(array) {
  return array.map(function(arr) {
    return arr.slice();
  });
}

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 ms,具体取决于随机数)。

如果将original_array的长度设置为10,000,000,则slice方法需要超过60 ms,而concat方法需要超过20 ms。


4
投票

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

基本示例:

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

References


3
投票

简单的解决方案:

original = [1,2,3]
cloned = original.map(x=>x)

2
投票
        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);

214
投票

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

myArray.slice(0);

比...更快

myArray.slice();

http://jsperf.com/cloning-arrays/3


118
投票

es6方式怎么样?

arr2 = [...arr1];

42
投票

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

var dup_array = JSON.parse(JSON.stringify(original_array))

27
投票
var cloned_array = [].concat(target_array);

25
投票

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

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


19
投票

a.map(e => e)是这项工作的另一种选择。截至今天,.map()在Firefox中速度非常快(几乎与.slice(0)一样快),但在Chrome中则不然。

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

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

11
投票

Fastest Way to Clone Array

我用这个非常简单的实用函数来测试克隆数组所需的时间。它不是100%可靠,但它可以为您提供克隆现有阵列需要多长时间的大量想法:

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

And tested different approach:

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

更新: 注意:除此之外,深度克隆数组的唯一方法是使用JSON.parse(JSON.stringify(arr))。 也就是说,如果你的数组可能包含函数,请不要使用上面的函数,因为它将返回null。 感谢@GilEpshtain的更新。


7
投票

这取决于浏览器。如果你看看Array.prototype.slice vs manual array creation的博客文章,那么每个人的表现都有一个粗略的指导:

Enter image description here

结果:

Enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.