Mar*_*aio 594 javascript arrays copy duplicates slice
为了在JavaScript中复制数组:以下哪个更快使用?
var dup_array = original_array.slice();
Run Code Online (Sandbox Code Playgroud)
For 环for(var i = 0, len = original_array.length; i < len; ++i)
dup_array[i] = original_array[i];
Run Code Online (Sandbox Code Playgroud)
我知道两种方式只做一个浅的副本:如果original_array包含对象的引用,则不会克隆对象,但只会复制引用,因此两个数组都将引用相同的对象.但这不是这个问题的重点.
我只询问速度.
Dan*_*Dan 739
克隆数组至少有5种(!)方法:
有一个huuuge BENCHMARKS线程,提供以下信息:
对于闪烁浏览器slice()是最快的方法,concat()有点慢,并且while loop慢2.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);
Run Code Online (Sandbox Code Playgroud)
切片
n = 1000*1000;
start = + new Date();
a = Array(n);
b = a.slice();
console.log(new Date() - start);
Run Code Online (Sandbox Code Playgroud)
请注意,这些方法将克隆Array对象本身,但是数组内容是通过引用复制的,并且不会被深度克隆.
origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true
Run Code Online (Sandbox Code Playgroud)
Kin*_*rog 226
技术上slice 是最快的方式.但是,如果添加0begin索引,它会更快.
myArray.slice(0);
Run Code Online (Sandbox Code Playgroud)
比...更快
myArray.slice();
Run Code Online (Sandbox Code Playgroud)
http://jsperf.com/cloning-arrays/3
Yuk*_*élé 122
es6方式怎么样?
arr2 = [...arr1];
Run Code Online (Sandbox Code Playgroud)
Vla*_*idi 43
深度克隆数组或对象的最简单方法:
var dup_array = JSON.parse(JSON.stringify(original_array))
Run Code Online (Sandbox Code Playgroud)
Saj*_*azy 29
var cloned_array = [].concat(target_array);
Run Code Online (Sandbox Code Playgroud)
lin*_*lnk 26
我整理了一个快速演示:http://jsbin.com/agugo3/edit
我在Internet Explorer 8上的结果是156,782和750,这表明slice在这种情况下要快得多.
Red*_*edu 19
a.map(e => e)是这项工作的另一种选择.到目前为止,在Firefox中.map()速度非常快(几乎一样快.slice(0)),但在Chrome中则不然.
另一方面,如果一个数组是多维的,因为数组是对象而对象是引用类型,所以切片或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));Run Code Online (Sandbox Code Playgroud)
Aay*_*rya 18
克隆对象数组的最快方法是使用扩展运算符
var clonedArray=[...originalArray]
or
var clonedArray = originalArray.slice(0); //with 0 index it's little bit faster than normal slice()
Run Code Online (Sandbox Code Playgroud)
但该克隆数组内的对象仍将指向旧的内存位置。因此,对 clonedArray 对象的更改也会更改 orignalArray。所以
var clonedArray = originalArray.map(({...ele}) => {return ele})
Run Code Online (Sandbox Code Playgroud)
这不仅会创建新数组,还会将对象克隆到其中。
如果您正在使用嵌套对象,则免责声明在这种情况下扩展运算符将作为浅克隆工作。那时最好使用
var clonedArray=JSON.parse(JSON.stringify(originalArray));
Run Code Online (Sandbox Code Playgroud)
Lio*_*rom 15
我用这个非常简单的实用函数来测试克隆数组所需的时间.它不是100%可靠,但它可以为您提供克隆现有阵列需要多长时间的大量想法:
function clone(fn) {
const arr = [...Array(1000000)];
console.time('timer');
fn(arr);
console.timeEnd('timer');
}
Run Code Online (Sandbox Code Playgroud)
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));
Run Code Online (Sandbox Code Playgroud)
更新:
注意:在所有这些中,深度克隆数组的唯一方法是使用JSON.parse(JSON.stringify(arr)).
也就是说,如果您的数组可能包含将返回的函数,请不要使用上述内容null.
感谢@GilEpshtain的更新.
ECMAScript 2015 方式与Spread操作符:
基本示例:
var copyOfOldArray = [...oldArray]
var twoArraysBecomeOne = [...firstArray, ..seccondArray]
Run Code Online (Sandbox Code Playgroud)
在浏览器控制台中尝试:
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);
Run Code Online (Sandbox Code Playgroud)
看看:链接.这不是速度,而是舒适.除此之外,您可以看到只能在基本类型上使用slice(0).
要创建数组的独立副本而不是refence的副本,可以使用数组切片方法.
例:
要创建数组的独立副本而不是refence的副本,可以使用数组切片方法.
Run Code Online (Sandbox Code Playgroud)var oldArray = ["mip", "map", "mop"]; var newArray = oldArray.slice();复制或克隆对象:
Run Code Online (Sandbox Code Playgroud)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);
来源:链接
正如@Dan所说:"这个答案变得过时了.使用基准来检查实际情况",jsperf有一个特定的答案,它没有自己的答案:while:
var i = a.length;
while(i--) { b[i] = a[i]; }
Run Code Online (Sandbox Code Playgroud)
获得960,589次/秒,亚军a.concat()为578,129次/秒,即60%.
这是最新的Firefox(40)64位.
@aleclarson创造了一个新的,更可靠的基准.
这取决于浏览器.如果您查看博客文章Array.prototype.slice与手动数组创建,那么每个程序的性能都有一个粗略的指导:
结果:
有一个更清洁的解决方案:
var srcArray = [1, 2, 3];
var clonedArray = srcArray.length === 1 ? [srcArray[0]] : Array.apply(this, srcArray);
Run Code Online (Sandbox Code Playgroud)
长度检查是必需的,因为Array构造函数在使用一个参数调用时行为不同.
记住.slice()不适用于二维数组.你需要这样的功能:
function copy(array) {
return array.map(function(arr) {
return arr.slice();
});
}
Run Code Online (Sandbox Code Playgroud)
基准时间!
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());Run Code Online (Sandbox Code Playgroud)
<textarea rows="50" cols="80" style="font-size: 16; resize:none; border: none;" id="log"></textarea>Run Code Online (Sandbox Code Playgroud)
自从您单击按钮后,基准测试将运行 10 秒。
我的结果:
Chrome(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)
Run Code Online (Sandbox Code Playgroud)
火狐(蜘蛛猴引擎):
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)
Run Code Online (Sandbox Code Playgroud)
获胜者代码:
function clone1(arr) {
return arr.slice(0);
}
Run Code Online (Sandbox Code Playgroud)
获胜引擎:
SpiderMonkey (Mozilla/Firefox)
这取决于阵列的长度.如果数组长度<= 1,000,000,则slice和concat方法大致相同.但是当你提供更广泛的范围时,该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();
Run Code Online (Sandbox Code Playgroud)
如果将original_array的长度设置为1,000,000,则slice方法和concat方法大约需要相同的时间(3-4 ms,具体取决于随机数).
如果将original_array的长度设置为10,000,000,则该slice方法将花费60 ms以上,并且该concat方法需要超过20 ms.