2011-12-22 25 views
10

Przewiń w dół, aby porównać z getById.getByClassName vs. qSA!


Gdybyśmy chcieli wybrać wszystkie elementy klasy "bar" które są wewnątrz elementu z ID "foo", możemy napisać tak:

$('#foo .bar') 

lub to:

$('.bar', '#foo') 

Nie są oczywiście inne metody, aby to osiągnąć, ale ze względu na to pytanie, porównajmy tylko te dwie metody.

Które z powyższych metod działają lepiej? (Który potrzebuje mniej czasu na wykonanie?)

Pisałem ten test wydajności:

(function() { 
    var i; 

    console.time('test1'); 
    for(i = 0; i < 100; i++) { 
     $('#question-mini-list .tags'); 
    } 
    console.timeEnd('test1'); 

    console.time('test2'); 
    for(i = 0; i < 100; i++) { 
     $('.tags', '#question-mini-list'); 
    } 
    console.timeEnd('test2'); 
})(); 

trzeba wykonać ją od wewnątrz konsoli na przepełnienie stosu w uruchomienia strony. Moje wyniki to:

Firefox:
test1: ~ 90ms
test2: ~ 18ms

Chrome:
test1: ~ 65ms
test2: ~ 30ms

Opera:
test1: ~ 50ms
test2: ~ 100ms

Tak więc w Firefox i Chrome, t Druga metoda jest wielokrotnie szybsza - tak jak się spodziewałem. Jednak w Operze sytuacja jest odwrotna. Zastanawiam się, co tu się dzieje.

Czy możesz przeprowadzić test na swoim komputerze i wyjaśnić, dlaczego Opera działa inaczej?


Aktualizacja

pisałem ten test, w celu zbadania, czy Opery QSA naprawdę jest super szybki. Jak się okazuje, jest.

(function() { 
    var i, limit = 5000, test1 = 'test1', test2 = 'test2'; 

    console.time(test1); 
    for(i = 0; i < limit; i += 1) { 
     document.getElementById('question-mini-list').getElementsByClassName('tags'); 
    } 
    console.timeEnd(test1); 

    console.time(test2); 
    for(i = 0; i < limit; i += 1) { 
     document.querySelectorAll('#question-mini-list .tags'); 
    } 
    console.timeEnd(test2); 
})(); 

Ponownie, należy uruchomić ten kod z poziomu konsoli na stronie początkowej stosu przepełnienia. Użyłem bookmarkletu Firebug Lite dla IE9 (ponieważ ta przeglądarka nie implementuje console.time).

Więc porównałem tą metodą:

document.getelementById('A').getElementsByClassName('B'); 

do tej metody:

document.querySelectorAll('#A .B'); 

Mam wykonywany Powyższy skrypt pięć razy z rzędu w każdej przeglądarce.Średnie arytmetyczne to:

enter image description here

(wszystkie liczby są w milisekundach).

Więc, wydajność pierwszej metody jest prawie taka sama w testowanych przeglądarek (16-36ms). Jednak, podczas gdy qSA jest znacznie wolniejsza w porównaniu do pierwszej metody, w Operze jest w rzeczywistości szybsza!

Tak, optymalizacja QSA jest możliwe, zastanawiam się, co innych przeglądarek czekają na ...

+0

'test1: 73ms',' test2: 11ms'. Opera jest dziwną przeglądarką, nie jestem pewna, dlaczego to się opóźnia. – Blender

+1

@Blender Proszę zwiększyć limit pętli. Mój laptop jest naprawdę powolny, więc poszedłem ze 100. Spróbuj 1000.(Wyniki mniejsze niż '4ms' nie są wiarygodne ...) –

+0

Czy rozważałeś włączenie' document.getElementById ('foo'). GetElementsByClassName ('bar') 'dla kompletności? – RobG

Odpowiedz

3

jQuery/Sizzle uniknie używania silnika Sizzle opartego na JavaScript, jeśli przeglądarka obsługuje querySelectorAll, i jeśli przekażemy poprawny selektor (bez niestandardowych selektorów nie CSS).

Oznacza to, że ostatecznie porównujesz implementacje querySelectorAll, zakładając, że testujesz przeglądarki, które je obsługują.

Istnieją inne optymalizacje, których używa jQuery lub Sizzle, więc jest to trudne, gdy porównujemy różne typy selekcji DOM w różnych przeglądarkach.

Powodem, dla którego wydajność Opery wydaje się być, że mają bardzo wysoce zoptymalizowaną implementację querySelectorAll. qSA, będąc stosunkowo nową metodą, nie była tak zoptymalizowana w niektórych przeglądarkach w porównaniu do starszych metod, takich jak getElementsByTagName.

+0

No cóż, qSA nie jest niczym nowym. Jest w Chrome od zawsze, w Firefoksie od 3.5, a w IE od 8. Przeglądarki miały mnóstwo czasu na jej optymalizację. Wstyd im (sprawdź mój nowy test powyżej). –

+0

@ ŠimeVidas: Tak, nowy tylko w stosunku do innych metod. Ale masz rację. Wydaje mi się, że mieli dużo czasu na optymalizację. Jestem pewien, że w końcu nadejdą. –

+0

@ ŠimeVidas: Zamierzam rzucić kolejną możliwość. Ponieważ twój test nic nie daje z wynikiem, zastanawiam się, czy optymalizacja nie przeszkadza w wyborze DOM. Być może wywoła tę metodę, ale zrezygnuje z wyszukiwania. Może to wyjaśniać, dlaczego pierwszy test trwa dłużej (dwa wywołania funkcji). To oczywiście czysta spekulacja. –

1

And the winner is ....

Test 3$('#question-mini-list').find('.tags');

  • test1: 25ms
  • test2: 19ms
  • test3: 10 ms

Dwie proponowane metody nie są równoważne.

test 1: Trzask analizuje od prawej do lewej (nie poprosić go, aby szukać kiedykolwiek element na stronie, a następnie ograniczyć do ID).

test 2: Używanie łańcucha jako kontekstu zazwyczaj nie ma sensu, używaj elementów jako kontekstu.

test 3: Wyszukiwanie elementów o identyfikatorze jest niesamowicie szybkie. Gdy już tam będziesz, możesz skoncentrować się na przedmiocie danej klasy.

+0

"* Sizzle parsuje od prawej do lewej *" - Chciałbym, żeby to było źródło. –

+1

Sizzle ma optymalizację, że jeśli ciąg zaczyna się od selektora identyfikatora, [używa go najpierw] (http://stackoverflow.com/questions/7757744/will-jquery-search-for-the-id-fefter-filter- inne-parametry-w-selektorze/7757792 # 7757792), więc powinny być równoważne. – Dennis

+0

jQuery przekształca '$ ('. Bar', '#foo')' na '$ ('# foo'). Find ('. Bar')' wewnętrznie. Uważam je za równoważne. Ten ostatni jest oczywiście nieco szybszy. –

1

Dla porównania, jest to 30x szybciej:

document.getElementById("foo").getElementsByClassName("bar"); 

See jsPerf: http://jsperf.com/jquery-selector-variations/3. To wymagałoby shim do pracy w starszych wersjach IE.

Podczas gdy jQuery jest niezwykle przydatny, jeśli prędkość jest najwyższa, nie zawsze jest to najlepsze narzędzie do pracy.

+0

Ten pasek "Zwykły JS" zrujnował wykres ': P' –

+0

@ ŠimeVidas - Tak, też to zauważyłem. Nie spodziewałem się tak dramatycznej różnicy. Możesz wrócić do poprzedniej wersji, jeśli chcesz tylko te. Rodzaj pokazuje, ile narzutów może być w obu analizach selektora ogólnego przeznaczenia i ogólnie w obiektach jQuery. – jfriend00

Powiązane problemy