Najbardziej efektywny sposób konwersji HTMLCollection na macierz


391

Czy istnieje bardziej skuteczny sposób konwersji HTMLCollection na macierz, inny niż iteracja przez zawartość wspomnianej kolekcji i ręczne wpychanie każdego elementu do tablicy?


10
Co należy rozumieć przez „wydajny”? Jeśli najlepiej się sprawdza, pętla for jest na ogół szybsza niż Array.prototype.slice . Pętla działa również w wielu różnych przeglądarkach (tj. We wszystkich), więc według tych kryteriów jest to „najbardziej wydajny sposób”. I to jest bardzo mały kod: for (var a=[], i=collection.length; i;) a[--i] = collection[i];tak mało „
oszustwa

@RobG Dziękuję - dałbym + 59k, gdybym mógł! ;-)
Slashback

1
Patrząc na obecną wydajność przeglądarki , plasterek głównie dogania pętle pod względem wydajności, z wyjątkiem Chrome. Przy użyciu większej liczby elementów i niewielkiej optymalizacji pętli wyniki są prawie identyczne , z wyjątkiem Chrome, w którym pętla jest znacznie szybsza.
RobG

Stworzyłem test jsperf, który analizuje obie metody wspomniane przez @harpo, a także test jquery wydajności. Odkryłem, że jquery jest nieco wolniejsze niż obie metody javascript, a najwyższa wydajność różni się w zależności od przypadków testowych js. Chrome 59.0.3071 / Mac OS X 10.12.5 woli używać, Array.prototype.slice.calla Brave (oparty na Chrome 59.0.3071) praktycznie nie ma różnicy między dwoma testami javascript w wielu uruchomieniach. Zobacz jsperf.com/htmlcollection-array-vs-jquery-children
NuclearPeon

jsben.ch/h2IFA => test wydajności najczęstszych sposobów na to
EscapeNetscape

Odpowiedzi:


696
var arr = Array.prototype.slice.call( htmlCollection )

będzie miał ten sam efekt przy użyciu „natywnego” kodu.

Edytować

Ponieważ uzyskuje się wiele wyświetleń, zwróć uwagę (na komentarz @ oriol), że następujące bardziej zwięzłe wyrażenie jest faktycznie równoważne:

var arr = [].slice.call(htmlCollection);

Zwróć jednak uwagę na komentarz @ JussiR, że w przeciwieństwie do formy „pełnej”, tworzy w tym procesie pustą, nieużywaną i rzeczywiście nieużywaną instancję tablicy. To, co robią kompilatory, jest poza zasięgiem programisty.

Edytować

Od ECMAScript 2015 (ES 6) istnieje również Array. Od :

var arr = Array.from(htmlCollection);

Edytować

ECMAScript 2015 udostępnia również operator rozkładania , który jest funkcjonalnie równoważny Array.from(chociaż uwaga, która Array.fromobsługuje funkcję odwzorowania jako drugi argument).

var arr = [...htmlCollection];

Potwierdziłem, że obie powyższe prace działają NodeList.

Porównanie wydajności wymienionych metod: http://jsben.ch/h2IFA


7
Nie udaje się to w IE6.
Heath Borders

29
Skrót [].slice.call(htmlCollection)również działa.
Oriol

1
@ChrisNielsen Tak. Zostałem o tym źle poinformowany. Przepraszam za rozpowszechnianie tego. Nie zdawałem sobie sprawy, że też to stwierdziłem. Usunąłem komentarz, aby uniknąć pomyłek, ale dla kontekstu przeczytałem (lub źle przeczytałem) gdzieś, że pocięcie HTMLCollection sprawiło, że zachowuje się on zarówno jako tablica, jak i kolekcja. Całkowicie niepoprawny.
Erik Reppen

3
Skrót [] .slice nie jest równoważny, ponieważ tworzy również nieużywaną pustą instancję tablicy. Nie jestem jednak pewien, czy kompilatory są w stanie go zoptymalizować.
JussiR

3
Array.from, tzn. fromnie jest obsługiwany przez IE11.
Frank Conijn

86

nie jestem pewien, czy jest to najbardziej wydajny, ale zwięzła składnia ES6 może wyglądać tak:

let arry = [...htmlCollection] 

Edycja: Kolejna, z komentarza Chris_F:

let arry = Array.from(htmlCollection)

9
Dodatkowo ES6 dodajeArray.from()
Chris_F

4
Uważaj na pierwszy, podczas transpozycji z babel występuje subtelny błąd, w którym [... htmlCollection] zwróci tablicę z htmlCollection, ponieważ jest to jedyny element.
Marcel M.,

3
Operator rozprzestrzeniania się macierzy nie działa na htmlCollection. Ma zastosowanie tylko do NodeList.
Bobby,

1
Array.from, tzn. fromnie jest obsługiwany przez IE11.
Frank Conijn

Benchmark Wygląda na to, że operator spreadu jest szybszy z tych 2.
RedSparr0w

20

Widziałem bardziej zwięzłą metodę pozyskiwania Array.prototypemetod, która działa równie dobrze. Przekształcanie HTMLCollectionobiektu w Arrayobiekt pokazano poniżej:

[] .slice.call (yourHTMLCollectionObject);

I, jak wspomniano w komentarzach, w przypadku starszych przeglądarek, takich jak IE7 i wcześniejsze, wystarczy użyć funkcji zgodności, takiej jak:

function toArray(x) {
    for(var i = 0, a = []; i < x.length; i++)
        a.push(x[i]);

    return a
}

Wiem, że to stare pytanie, ale czułem, że zaakceptowana odpowiedź była nieco niepełna; więc pomyślałem, że wyrzucę to FWIW.


6

W przypadku implementacji w różnych przeglądarkach sugeruję, abyś spojrzał na funkcję prototype.js $A

skopiowane z 1.6.1 :

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

Prawdopodobnie nie używa, Array.prototype.sliceponieważ nie jest dostępny w każdej przeglądarce. Obawiam się, że wydajność jest dość zła, ponieważ istnieje odwrócenie pętli javascript nad iterable.


2
OP poprosił o inny sposób niż „iterowanie po zawartości wspomnianej kolekcji i ręczne wpychanie każdego elementu do tablicy”, ale to właśnie ta $Afunkcja wykonuje większość czasu.
Luc125

1
Myślę, że próbowałem zrobić to, że nie ma dobrego sposobu, aby to zrobić, kod prototype.js pokazuje, że możesz poszukać metody „toArray”, ale w przypadku braku tej iteracji najbezpieczniejsza trasa
Gareth Davis

1
Spowoduje to utworzenie nowych, nieokreślonych członków w rzadkich tablicach. Przed przypisaniem powinien istnieć test hasOwnProperty .
RobG

3

To jest moje osobiste rozwiązanie, oparte na informacjach tutaj (ten wątek):

var Divs = new Array();    
var Elemns = document.getElementsByClassName("divisao");
    try {
        Divs = Elemns.prototype.slice.call(Elemns);
    } catch(e) {
        Divs = $A(Elemns);
    }

Gdzie $ A został opisany przez Garetha Davisa w jego poście:

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

Jeśli przeglądarka obsługuje najlepszy sposób, ok, w przeciwnym razie będzie korzystać z przeglądarki krzyżowej.


Ogólnie rzecz biorąc, nie oczekuję, że try / catch będzie skutecznym sposobem zarządzania przepływem kontroli. Możesz sprawdzić, czy funkcja istnieje najpierw, a następnie uruchomić jedną lub drugą nieco taniej.
Patrick,

2
Podobnie jak w przypadku odpowiedzi Garetha Davisa, tworzy to nowych, nieokreślonych członków w rzadkich tablicach, tak się [,,]dzieje [undefined, undefined].
RobG

Nie miałem jeszcze takich problemów. Wygląda na to, że kolekcja 3 elementów daje tablicę z 2 elementami. Jeśli chodzi o puste, to jest niezdefiniowane, to jest trochę ograniczeń JavaScript, domyślam się, że spodziewałeś się wartości null zamiast niezdefiniowanej, prawda?
Gustavo,

3

Działa to we wszystkich przeglądarkach, w tym we wcześniejszych wersjach IE.

var arr = [];
[].push.apply(arr, htmlCollection);

Ponieważ jsperf jest obecnie niedostępny, oto jsfiddle, który porównuje wydajność różnych metod. https://jsfiddle.net/qw9qf48j/


spróbujvar args = (htmlCollection.length === 1 ? [htmlCollection[0]] : Array.apply(null, htmlCollection));
Shahar Shokrani,

3

Aby skutecznie przekonwertować tablicę na tablicę, możemy skorzystać z jQuery makeArray :

makeArray: Konwertuj obiekt podobny do tablicy na prawdziwą tablicę JavaScript.

Stosowanie:

var domArray = jQuery.makeArray(htmlCollection);

Trochę więcej:

Jeśli nie chcesz zachować odwołania do obiektu tablicy (przez większość czasu HTMLCollections zmieniają się dynamicznie, więc lepiej skopiować je do innej tablicy, w tym przykładzie zwróć szczególną uwagę na wydajność:

var domDataLength = domData.length //Better performance, no need to calculate every iteration the domArray length
var resultArray = new Array(domDataLength) // Since we know the length its improves the performance to declare the result array from the beginning.

for (var i = 0 ; i < domDataLength ; i++) {
    resultArray[i] = domArray[i]; //Since we already declared the resultArray we can not make use of the more expensive push method.
}

Co to jest tablica?

HTMLCollection jest "array-like"przedmiotem, array-jak obiekty są podobne do obiektu Array, ale brakuje wiele jego definicji funkcjonalnym:

Obiekty podobne do tablic wyglądają jak tablice. Mają różne numerowane elementy i właściwość długości. Ale tam kończy się podobieństwo. Obiekty podobne do macierzy nie mają żadnych funkcji macierzy, a pętle wejściowe nawet nie działają!

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.