JavaScript NodeList를 Array로 변환하는 가장 빠른 방법은 무엇입니까?


251

이전에 답변 된 질문에 따르면 이것이 가장 빠른 방법이라고합니다.

//nl is a NodeList
var arr = Array.prototype.slice.call(nl);

내 브라우저에서 벤치마킹 할 때 이보다 3 배 이상 느립니다.

var arr = [];
for(var i = 0, n; n = nl[i]; ++i) arr.push(n);

둘 다 동일한 출력을 생성하지만 특히 사람들이 다르게 말했기 때문에 두 번째 버전이 가장 빠른 방법이라고 믿기가 어렵습니다.

내 브라우저 (Chromium 6)의 기발한가요? 아니면 더 빠른 방법이 있습니까?

편집 : 관심있는 사람은 다음을 결정했습니다 (테스트 한 모든 브라우저에서 가장 빠른 것 같습니다).

//nl is a NodeList
var l = []; // Will hold the array of Node's
for(var i = 0, ll = nl.length; i != ll; l.push(nl[i++]));

EDIT2 : 더 빠른 방법을 찾았습니다

// nl is the nodelist
var arr = [];
for(var i = nl.length; i--; arr.unshift(nl[i]));

2
arr[arr.length] = nl[i];arr.push(nl[i]);함수 호출을 피하기 때문에 보다 빠를 수 있습니다 .
Luc125

9
이 jsPerf 페이지는이 페이지의 모든 답변을 추적하고 있습니다 : jsperf.com/nodelist-to-array/27
pilau

IE8에서는 "EDIT2 : 더 빠른 방법을 찾았습니다"가 92 % 느립니다.
Camilo Martin

2
이미 알고있는 노드 수를 알고 있으므로 다음과 같습니다.var i = nl.length, arr = new Array(i); for(; i--; arr[i] = nl[i]);
mems

@ Luc125 그것은 푸시 구현이 최적화 될 수 있기 때문에 브라우저에 달려 있습니다 .v8이 이런 종류의 것들에 좋기 때문에 크롬에 대해 생각하고 있습니다.
axelduch

답변:


197

두 번째 브라우저는 일부 브라우저에서 더 빠르지 만 주요 요점은 첫 번째 브라우저가 브라우저 간이 아니기 때문에 사용해야한다는 것입니다. 비록 그들이 타임즈 임에도 불구하고

@kangax ( IE 9 미리보기 )

Array.prototype.slice 는 이제 특정 호스트 객체 (예 : NodeList)를 배열로 변환 할 수 있습니다. 이는 대부분의 최신 브라우저에서 꽤 오랫동안 할 수 있었던 것입니다.

예:

Array.prototype.slice.call(document.childNodes);

??? 둘 다 브라우저 간 호환 가능-Javascript (적어도 ECMAscript 사양과 호환된다고 주장하는 경우)는 Javascript입니다. 배열, 프로토 타입, 슬라이스 및 호출은 모두 핵심 언어 + 객체 유형의 기능입니다.
Jason S

6
그러나 IE의 NodeLists에서는 사용할 수 없습니다 (
빨리

9
NodeLists 언어의 일부가 아니기 때문에, 그들은 특히 IE의 버그 / 예측할 수없는 것으로 알려져있다 DOM의 API의 일부입니다
gblazex

3
IE8을 고려하면 Array.prototype.slice는 브라우저 간이 아닙니다.
Lajos Meszaros

1
예, 그것이 기본적으로 내 대답이었습니다. :) 오늘 (2015)보다 2010 년에 더 관련이 있었지만.
gblazex

224

ES6을 사용하면 NodeList에서 Array.from()함수 를 만드는 간단한 방법이 생겼습니다 .

// nl is a NodeList
let myArray = Array.from(nl)

이 새로운 ES6 방법은 위에서 언급 한 다른 방법과 속도를 어떻게 비교합니까?
user5508297

10
@ user5508297 슬라이스 호출 트릭보다 낫습니다. for 루프보다 느리지 만, 배열을 순회하지 않고 배열을 원할 수도 있습니다. 그리고 구문은 아름답고 간단하며 기억하기 쉽습니다!
webdif

Array.from의 좋은 점은 map 인수를 사용할 수 있다는 것입니다.console.log(Array.from([1, 2, 3], x => x + x)); // expected output: Array [2, 4, 6]
honzajde


19

일부 최적화 :

  • NodeList의 길이를 변수에 저장
  • 설정하기 전에 새 배열의 길이를 명시 적으로 설정하십시오.
  • 밀거나 움직이지 않고 인덱스에 액세스하십시오.

코드 ( jsPerf ) :

var arr = [];
for (var i = 0, ref = arr.length = nl.length; i < ref; i++) {
 arr[i] = nl[i];
}

배열을 만든 다음 별도로 크기를 조정하는 대신 Array (length)를 사용하여 시간을 조금 더 줄일 수 있습니다 . 그런 다음 const를 사용하여 루프 내부에 let을 사용하여 배열과 길이를 선언하면 위의 방법보다 약 1.5 % 빠릅니다. const a = Array (nl.length), c = a.length; for (let b = 0; b <c; b ++) {a [b] = nl [b]; } jsperf.com/nodelist-to-array/93
BrianFreud 1

15

결과는 브라우저에 전적으로 의존하고 객관적인 평결을 내기 위해 성능 테스트를해야합니다. 여기에 몇 가지 결과가 있습니다. 여기에서 실행할 수 있습니다 .

크롬 6 :

Firefox 3.6 :

Firefox 4.0b2 :

사파리 5 :

IE9 플랫폼 미리보기 3 :


1
리버스 for 루프가 이것들에 어떻게 견디는 지 궁금합니다 ... for (var i=o.length; i--;)...이 테스트에서 'for 루프'는 모든 반복에서 길이 속성을 재평가 했습니까?
Dagg Nabbit

14

가장 빠르고 교차 브라우저는

for(var i=-1,l=nl.length;++i!==l;arr[i]=nl[i]);

내가 비교할 때

http://jsbin.com/oqeda/98/edit

* 아이디어에 대한 @CMS 감사합니다!

크롬 (Chrome과 유사) Firefox 오페라


1
언급 한 테스트를 포함하려면 링크가 잘못 된 것 같습니다 .89 대신 89이어야합니다. 그리고 98은 가장 완벽한 것 같습니다.
Yaroslav Yakovlev

9

ES6에서는 다음 중 하나를 사용할 수 있습니다.

  • 배열

    let array = Array.from(nodelist)

  • 스프레드 연산자

    let array = [...nodelist]


이전 답변에서 이미 작성된 것을 새로 추가하지 않습니다.
Stefan Becker

7
NodeList.prototype.forEach = Array.prototype.forEach;

이제 document.querySelectorAll ( 'div'). forEach (function () ...) 할 수 있습니다


좋은 생각입니다, @John 감사합니다! 그러나 NodeList작동하지 않지만 다음 Object과 같습니다. Object.prototype.forEach = Array.prototype.forEach; document.getElementsByTagName("img").forEach(function(img) { alert(img.src); });
Ian Campbell

3
Object.prototype을 사용하지 마십시오 : JQuery와 사전 리터럴 구문과 같은 많은 것들을 손상시킵니다.
Nate Symer

물론 기본 제공 내장 함수를 확장하지 마십시오.
roland

5

더 빠르고 더 짧은 :

// nl is the nodelist
var a=[], l=nl.length>>>0;
for( ; l--; a[l]=nl[l] );

3
>>>0? 그리고 for 루프의 첫 번째 부분에 과제를 두지 않겠습니까?
Camilo Martin

5
또한 이것은 버그입니다. 때 l이다 0, 루프 그러므로, 종료됩니다 0번째 요소는 복사되지 않습니다 (remeber 인덱스의 요소가있다 0)
카밀로 마틴

1
이 대답을 사랑하지만 ... 궁금 사람이라면 다음이 >>> 있습니다 여기에 필요하지 않을 수 있지만, 배열 사양에 노드 목록의 길이가 부착 된을 보장하기 위해 사용된다; 부호없는 32 비트 정수인지 확인합니다. 여기를 확인 ecma-international.org/ecma-262/5.1/#sec-15.4 , 읽을 수없는 코드이 마음에 CamiloMartin의 제안을 @로이 방법을 사용!
Todd

@CamiloMartin에 대한 답글- '가변 호이 스팅'으로 인해 루프 var의 첫 번째 부분 을 삽입하는 것은 위험합니다 for. 선이 아래로 어딘가에 나타나 var더라도 의 선언은 함수 상단에 '게양'되며, var이로 인해 코드 시퀀스에서 분명하지 않은 부작용이 발생할 수 있습니다. 예를 들어, 동일한 기능의 일부 코드가 발생 하기 전에 for 루프 수도 의존al미표시된다. 따라서 예측 가능성을 높이려면 함수 상단에 vars를 선언하십시오 (또는 ES6의 경우 호이스트를 사용하지 const않거나 let대신 사용하십시오 ).
brennanyoung

3

이 블로그 게시물 확인 여기에 같은 일에 대해 그 대화를. 내가 수집 한 것에서 추가 시간은 스코프 체인을 걷는 것과 관련이있을 수 있습니다.


흥미 롭군 방금 비슷한 테스트를 수행했으며 Firefox 3.6.3은 속도가 향상되지 않았지만 Opera 10.6은 20 % 증가했으며 Chrome 6은 수동 반복 푸시 방식으로 230 % (!) 증가했습니다.
jairajs89

@ jairajs89 매우 이상합니다. 이 나타납니다 Array.prototype.slice브라우저에 의존합니다. 각 브라우저가 사용하는 알고리즘이 궁금합니다.
Vivin Paliath

3

이것은 내 JS에서 사용하는 기능입니다.

function toArray(nl) {
    for(var a=[], l=nl.length; l--; a[l]=nl[l]);
    return a;
}

1

다음은이 게시 날짜를 기준으로 업데이트 된 차트입니다 ( "알 수없는 플랫폼"차트는 Internet Explorer 11.15.16299.0입니다).

사파리 11.1.2 파이어 폭스 61.0 크롬 68.0.3440.75 Internet Explorer 11.15.16299.0

이 결과에서 사전 할당 1 방법이 가장 안전한 크로스 브라우저 내기 인 것으로 보입니다.


1

가정하면 nodeList = document.querySelectorAll("div"), 이것은 간결한 형태의 nodelist배열 로 변환 하는 것입니다.

var nodeArray = [].slice.call(nodeList);

여기에서 사용 하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.