Najszybszy sposób na konwersję JavaScript NodeList na Array?


251

Wcześniej odpowiedzi na pytania tutaj powiedział, że był to najszybszy sposób:

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

Podczas testów porównawczych w mojej przeglądarce stwierdziłem, że jest on ponad 3 razy wolniejszy niż to:

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

Oba generują tę samą moc wyjściową, ale trudno mi uwierzyć, że moja druga wersja jest najszybszym możliwym sposobem, zwłaszcza że ludzie mówią tutaj inaczej.

Czy to dziwactwo w mojej przeglądarce (Chromium 6)? Czy jest szybszy sposób?

EDYCJA: Dla każdego, kogo to obchodzi, zdecydowałem się na następujące (co wydaje się najszybsze w każdej testowanej przeglądarce):

//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: Znalazłem jeszcze szybszy sposób

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

2
arr[arr.length] = nl[i];może być szybszy niż, arr.push(nl[i]);ponieważ pozwala uniknąć wywołania funkcji.
Luc125

9
Ta strona jsPerf śledzi wszystkie odpowiedzi na tej stronie: jsperf.com/nodelist-to-array/27
pilau

Pamiętaj, że „EDIT2: znalazłem szybszy sposób” jest o 92% wolniejszy na IE8.
Camilo Martin

2
Ponieważ wiesz już, ile masz węzłów:var i = nl.length, arr = new Array(i); for(; i--; arr[i] = nl[i]);
mem

@ Luc125 To zależy od przeglądarki, ponieważ implementacja wypychania może być zoptymalizowana, myślę o chrome, ponieważ v8 jest dobra z tego rodzaju rzeczami.
axelduch

Odpowiedzi:


197

Drugi jest zazwyczaj szybszy w niektórych przeglądarkach, ale najważniejsze jest to, że musisz go używać, ponieważ pierwszy nie jest po prostu przeznaczony dla różnych przeglądarek. Nawet jeśli czasy się zmieniają

@kangax ( podgląd IE 9 )

Array.prototype.slice może teraz konwertować niektóre obiekty hosta (np. NodeList) na tablice - coś, co większość współczesnych przeglądarek była w stanie zrobić od dłuższego czasu.

Przykład:

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

??? Oba są kompatybilne z różnymi przeglądarkami - Javascript (przynajmniej jeśli twierdzi, że jest zgodny ze specyfikacją ECMAscript) to Javascript; Tablica, prototyp, plasterek i wywołanie to cechy podstawowego języka + typy obiektów.
Jason S

6
ale nie można ich używać na NodeLists w IE (wiem, że to do bani, ale hej, zobacz moją aktualizację)
gblazex

9
ponieważ NodeLists nie są częścią języka, są częścią interfejsu API DOM, o którym wiadomo, że jest
błędny

3
Array.prototype.slice nie działa w różnych przeglądarkach, jeśli weźmiesz pod uwagę IE8.
Lajos Meszaros

1
Tak, o to właściwie chodziło w mojej odpowiedzi :) Chociaż była bardziej trafna w 2010 roku niż w dzisiejszym (2015).
gblazex

224

W ES6 mamy teraz prosty sposób na utworzenie tablicy z NodeList: Array.from()funkcję.

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

Jak ta nowa metoda ES6 wypada szybciej niż inne, o których wspomniano powyżej?
user5508297,

10
@ user5508297 Lepszy niż sztuczka wywołania połączenia. Wolniej niż w przypadku pętli for, ale nie do końca możemy chcieć mieć tablicę bez przechodzenia przez nią. A składnia jest piękna, prosta i łatwa do zapamiętania!
webdif

Zaletą Array.from jest to, że można użyć argumentu map:console.log(Array.from([1, 2, 3], x => x + x)); // expected output: Array [2, 4, 6]
honzajde


19

Niektóre optymalizacje:

  • zapisz długość NodeList w zmiennej
  • jawnie ustaw nową długość tablicy przed ustawieniem.
  • dostęp do indeksów, zamiast wypychania lub cofania.

Kod ( jsPerf ):

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

Możesz ogolić się trochę dłużej, używając tablicy (długości) zamiast tworzyć tablicę, a następnie osobno ją skalować. Jeśli następnie użyjesz const do zadeklarowania tablicy i jej długości, z letem wewnątrz pętli, kończy się ona około 1,5% szybciej niż powyższa metoda: const a = Array (nl.length), c = a.length; dla (niech b = 0; b <c; b ++) {a [b] = nl [b]; } patrz jsperf.com/nodelist-to-array/93
BrianFreud

15

Wyniki będą całkowicie zależeć od przeglądarki, aby dać obiektywny werdykt, musimy wykonać testy wydajności, oto kilka wyników, które możesz uruchomić tutaj :

Chrome 6:

Firefox 3.6:

Firefox 4.0b2:

Safari 5:

Przegląd platformy IE9 3:


1
Zastanawiam się, jak odwrotna pętla for wytrzymuje te ... for (var i=o.length; i--;)... czy „for loop” w tych testach przewartościowała właściwość długości przy każdej iteracji?
Dagg Nabbit


9

W ES6 możesz użyć:

  • Array.from

    let array = Array.from(nodelist)

  • Operator rozsiewu

    let array = [...nodelist]


Nie dodaje nic nowego, co już napisano w poprzednich odpowiedziach.
Stefan Becker

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

Teraz możesz zrobić document.querySelectorAll ('div'). ForEach (function () ...)


Dobry pomysł, dzięki @John! Jednak NodeListnie działa, ale Object: Object.prototype.forEach = Array.prototype.forEach; document.getElementsByTagName("img").forEach(function(img) { alert(img.src); });
Ian Campbell

3
Nie używaj Object.prototype: łamie JQuery i mnóstwo rzeczy takich jak składnia dosłowna słownika.
Nate Symer

Jasne, unikaj rozszerzania wbudowanych funkcji natywnych.
roland

5

szybciej i krócej:

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

3
Dlaczego >>>0? A dlaczego nie umieścić zadań w pierwszej części pętli for?
Camilo Martin

5
Ponadto jest to błędne. Kiedy ljest 0, pętla się kończy, dlatego 0element th nie zostanie skopiowany (pamiętaj, że element ma indeks 0)
Camilo Martin

1
Uwielbiam tę odpowiedź, ale ... Każdy, kto się zastanawia: >>> może nie być tutaj konieczny, ale służy do zagwarantowania długości listy węzłów zgodnie ze specyfikacją tablicy; zapewnia, że ​​jest to 32-bitowa liczba całkowita bez znaku. Sprawdź tutaj ecma-international.org/ecma-262/5.1/#sec-15.4 Jeśli podoba Ci się nieczytelny kod, użyj tej metody z sugestiami @ CamiloMartin!
Todd

W odpowiedzi na @CamiloMartin - włożenie vardo pierwszej części forpętli jest ryzykowne z powodu „zmiennego podnoszenia”. Deklaracja varzostanie „podniesiona” na górę funkcji, nawet jeśli varlinia pojawi się gdzieś niżej, co może powodować skutki uboczne, które nie są oczywiste z sekwencji kodu. Na przykład niektóre kodu w tej samej funkcji występujący przed dla pętli może zależeć na ai ljest nielegalnej. Dlatego, aby uzyskać większą przewidywalność, zadeklaruj swoje zmienne na górze funkcji (lub jeśli na ES6, użyj constlub letzamiast tego, które nie podnoszą).
brennanyoung

3

Sprawdź ten post na blogu , który mówi o tym samym. Z tego, co zbieram, dodatkowy czas może mieć związek z przejściem w górę łańcucha lunety.


Ciekawy. Właśnie zrobiłem teraz kilka podobnych testów i Firefox 3.6.3 nie pokazuje wzrostu prędkości, robiąc to w obu kierunkach, podczas gdy Opera 10.6 ma wzrost o 20%, a Chrome 6 ma wzrost o 230% (!), Wykonując to ręcznie.
jairajs89

@ jairajs89 dość dziwne. Wygląda na Array.prototype.sliceto, że zależy od przeglądarki. Zastanawiam się, jakiego algorytmu używa każda z przeglądarek.
Vivin Paliath


1

Oto wykresy zaktualizowane na dzień opublikowania (wykres „nieznanej platformy” to Internet Explorer 11.15.16299.0):

Safari 11.1.2 Firefox 61.0 Chrome 68.0.3440.75 Internet Explorer 11.15.16299.0

Na podstawie tych wyników wydaje się, że metoda preallocate 1 jest najbezpieczniejszym zakładem dla różnych przeglądarek.


1

Zakładając nodeList = document.querySelectorAll("div"), że jest to zwięzła forma konwersji nodelistna tablicę.

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

Zobacz mnie, użyj go tutaj .

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.