자바 스크립트에서 배열을 복제하는 가장 빠른 방법-슬라이스 대 'for'루프
JavaScript에서 배열을 복제하려면 다음 중 사용하는 것이 더 빠릅니까?
슬라이스 방법
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에 개체에 대한 참조가 포함되어 있으면 개체는 복제되지 않지만 참조 만 복사되므로 두 배열 모두 동일한 개체에 대한 참조를 갖게됩니다. 그러나 이것은이 질문의 요점이 아닙니다.
나는 속도에 대해서만 묻는 것입니다.
어레이를 복제하는 방법 은 최소한 5 개 (!)입니다.
- 고리
- 일부분
- Array.from ()
- 연결
- 확산 연산자 (FASTEST)
다음 정보를 제공 하는 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);
일부분
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
기술적 slice
으로 가장 빠른 방법입니다. 그러나0
시작 색인 을 추가하면 더 빠릅니다 .
myArray.slice(0);
보다 빠릅니다
myArray.slice();
http://jsperf.com/cloning-arrays/3
es6 방식은 어떻습니까?
arr2 = [...arr1];
어레이 또는 객체를 딥 클론하는 가장 쉬운 방법 :
var dup_array = JSON.parse(JSON.stringify(original_array))
var cloned_array = [].concat(target_array);
간단한 데모를 만들었습니다 : http://jsbin.com/agugo3/edit
Internet Explorer 8에서 내 결과는 156, 782 및 750으로, slice
이 경우 훨씬 더 빠릅니다.
a.map(e => e)
이 직업의 또 다른 대안입니다. 오늘 현재 Firefox에서는 .map()
매우 빠르지 .slice(0)
만 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));
어레이를 복제하는 가장 빠른 방법
배열을 복제하는 데 걸리는 시간을 테스트하기 위해이 매우 단순한 유틸리티 함수를 만들었습니다. 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));
업데이트 :
참고 : 그중에서 배열을 딥 클론하는 유일한 방법은 JSON.parse(JSON.stringify(arr))
.
즉, 배열에를 반환하므로 함수가 포함될 수있는 경우 위를 사용하지 마십시오 null
.
이 업데이트에 대해 @GilEpshtain에게 감사드립니다 .
@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은 새롭고 더 안정적인 벤치 마크를 만들었습니다.
보세요 : 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);
출처 : 링크
브라우저에 따라 다릅니다. 블로그 게시물 Array.prototype.slice 대 수동 어레이 생성 을 살펴보면 각각의 성능에 대한 대략적인 가이드가 있습니다.
결과 :
훨씬 더 깨끗한 솔루션이 있습니다.
var srcArray = [1, 2, 3];
var clonedArray = srcArray.length === 1 ? [srcArray[0]] : Array.apply(this, srcArray);
Array
생성자가 정확히 하나의 인수로 호출 될 때 다르게 동작 하기 때문에 길이 확인이 필요합니다 .
.slice ()는 2 차원 배열에서는 작동하지 않습니다. 다음과 같은 기능이 필요합니다.
function copy(array) {
return array.map(function(arr) {
return arr.slice();
});
}
어레이의 길이에 따라 다릅니다. 배열 길이가 <= 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();
original_array의 길이를 1,000,000으로 설정하면 slice
메서드와 concat
메서드가 거의 같은 시간 (난수에 따라 3-4ms)이 걸립니다.
original_array의 길이를 10,000,000으로 설정하면 slice
메서드는 60ms concat
이상 걸리고 메서드는 20ms 이상 걸립니다.
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);
참고 문헌
간단한 해결책 :
original = [1,2,3]
cloned = original.map(x=>x)
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);
벤치 마크 시간!
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 초 동안 실행됩니다.
내 결과 :
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)
Firefox (SpiderMonkey 엔진) :
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);
}
우승자 엔진 :
SpiderMonkey (Mozilla / Firefox)
'development' 카테고리의 다른 글
배열을 인라인으로 선언하는 방법이 있습니까? (0) | 2020.10.03 |
---|---|
What characters are allowed in an email address? (0) | 2020.10.03 |
HTML 파일에 다른 HTML 파일 포함 (0) | 2020.10.03 |
JavaScript에서 문자열을 날짜로 변환 (0) | 2020.10.03 |
확인되지 않은 캐스트 경고는 어떻게 해결합니까? (0) | 2020.10.03 |