关于在javascript中复制数组的最快方法:slice与”for”循环

Fastest way to duplicate an array in JavaScript - slice vs. 'for' loop

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

切片法

1
var dup_array = original_array.slice();

For

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

我知道这两种方法都只做一个简单的复制:如果原始数组包含对对象的引用,则不会克隆对象,但只复制引用,因此两个数组都将具有对同一对象的引用。但这不是这个问题的重点。

我只问速度。


至少有5个!!)克隆阵列的方法:

  • 数组(从)
  • 康塔特
  • 排列运算符(最快)

有一个Huuuge基准线程,提供了以下信息:

  • 对于Blink浏览器来说,slice()是最快的方法,concat()慢一些,while loop慢2.4倍。

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

这一点在2016年7月仍然有效。

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

while循环

1
2
3
4
5
6
7
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);

1
2
3
4
5
n = 1000*1000;
start = + new Date();
a = Array(n);
b = a.slice();
console.log(new Date() - start);

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

1
2
origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true


从技术上讲,slice是最快的方法。但是,如果添加0begin索引,速度会更快。

1
myArray.slice(0);

比快

1
myArray.slice();

http://jspef.com/cloning-array/3


ES6路怎么样?

1
arr2 = [...arr1];


最简单的深度克隆阵列或对象方法:

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


1
var cloned_array = [].concat(target_array);


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

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


a.map(e => e)是这项工作的另一种选择。到今天为止,在火狐浏览器中,.map()的速度非常快(几乎和.slice(0)的速度一样快),而在Chrome浏览器中则没有。

另一方面,如果一个数组是多维的,因为数组是对象,对象是引用类型,那么slice或concat方法都不能解决这个问题。因此,克隆阵列的一个正确方法是Array.prototype.clone()的发明,如下所示。

1
2
3
4
5
6
7
8
9
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));


最快的克隆阵列方法

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

1
2
3
4
5
6
function clone(fn) {
    const arr = [...Array(1000000)];
    console.time('timer');
    fn(arr);
    console.timeEnd('timer');
}

测试了不同的方法:

1
2
3
4
5
6
7
8
9
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进行此更新。


这取决于浏览器。如果您查看blog post array.prototype.slice vs manual array creation,可以大致了解每种方法的性能:

Enter image description here

结果:

Enter image description here


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

1
2
3
4
5
function copy(array) {
  return array.map(function(arr) {
    return arr.slice();
  });
}


看看:link。不是速度,而是舒适。此外,正如您所看到的,您只能对基元类型使用slice(0)。

要创建数组的独立副本而不是引用的副本,可以使用数组切片方法。

例子:

To make an independent copy of an array rather than a copy of the refence to it, you can use the array slice method.

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

To copy or clone an object :

1
2
3
4
5
6
7
8
9
10
11
12
13
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);

资料来源:链接


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

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

需要进行长度检查,因为当使用一个参数调用Array构造函数时,它的行为不同。


正如@dan所说,"这个答案很快就过时了。使用基准来检查实际情况",JSPerf有一个特定的答案本身没有答案:while:

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

运行速度为960589次/秒,运行速度为578129次/秒,为60%。

这是最新的64位火狐(40)。

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


它取决于数组的长度。如果数组长度小于等于1000000,则sliceconcat方法所用的时间大致相同。但当你给出更大的范围时,concat方法获胜。

例如,尝试以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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();

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

如果将原始数组的长度设置为10000000,则slice方法将超过60 ms,而concat方法将超过20 ms。


ecmascript 2015与Spread运营商的合作方式:

基本实例:

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

在浏览器控制台中尝试:

1
2
3
4
5
6
7
8
9
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);

工具书类

  • 6扩散算子的伟大用途
  • 扩展句法


一个简单的解决方案:

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