jQuery vs Vanilla JS featuring JSLint = good practice

Javascript Frameworks wie jQuery((jQuery – „write less, do more“)) erleichtern jedem Entwickler die Arbeit, aber können dem Browser mehr Arbeit bescheren.

JSLint((JSLint – The JavaScript Code Quality Tool)) ist in der JavaScript Entwicklung ein idealer Partner, doch leider ist jQuery für JSLint nicht ganz durchschaubar und so können sich schnell wieder „bad practices“ einschleichen.

Zu diesem Beitrag habe ich mir ein Testdokument erstellt, welches aus einem simplen html Gerüst besteht und als Inhalt einen <ul> mit 10.000 <li> Elementen besteht, welche jeweils ein Click Event bekommen um eine Klasse zu wechseln. Das erste Beispiel nutzt jQuery 1.7.2, alle weiteren jQuery 1.11.2. UPDATE: Nun habe ich auch eine Vanilla JS delegated Methode durchgemessen. UPDATE 2: Die kompletten Testdateien habe ich nun bei bitbucket hochgeladen: djpogo/vanilla-jquery-listener.

Bei jQuery ist es durchaus gängig einen Listener Funktion als anonyme Funktion zu implementieren. Das sieht dann so aus:

// jQuery &lt; = 1.7.x 
$(".viewportChanger").click(function () { $(this).toggleClass("show"); });
 
// jQuery 1.11.2 ohne Delegation
$(".viewportChanger").on("click", function () { $(this).toggleClass("show"); });
 
// jQuery 1.11.2 mit Delegation
$(".viewport").on("click", ".viewportChanger", function () { $(this).toggleClass("show"); });

Die Beispiele zeigen Wege wie man das Problem lösen kann. Mit der alten jQuery <= 1.7.2 Syntax werden alle 10.000 <li> ausgewählt und für jedes <li> wird die anonyme Click Listener Funktion instantiiert. Das gleiche passiert bei dem Code 1.11.2 ohne Delegation.

Der letzte Ansatz arbeitet mit Delegation, das bedeutet man setzt auf einem übergeordneten Element den Listener und trifft dann im zweiten Parameter noch eine genauere Auswahl. Das positive daran ist, dass die Click Listener Funktion nur einmal instantiiert wird.

Alle Ansätze winkt JSLint problemlos durch, da JSLint weder die Syntax der „.click()“ oder der „.on()“ Funktion versteht.

Im Chrome Dev Tool habe ich die Beispiele durchgemessen und folgende Werte herausgefunden:

AnsatzPageLoadScript VerarbeitungInstallierte ListenerSpeichernutzungScreenshot
jQuery 1.7.21.59 s424.910 ms10.00211.6 MB – 24.3 MBbad-jquery2
jQuery 1.11.21.66 s383.796 ms10.00211.8 MB – 25.2 MBjquery-better2
jQuery 1.11.2 & Delegation1.17 s55.375 ms33.8 MB – 5.8 MBjquery-opt2
Vanilla JS1.21 s26.536 ms25.8 MB – 6.7 MBvanilla-js-2
Vanilla JS delegated1.42 s17.393 ms23.5 MB – 4.9 MBvanilla-js-opt-2

Bei dem gleichen Code in Vanilla JS weist JSLint direkt darauf hin, dass man innerhalb einer Schleife keine Funktion deklarieren soll. Dank dieses Hinweises (und bei entsprechender Grunt Konfiguration der Abbruch der Verarbeitung) wird man gezwungen den Click Listener als eigene Funktion zu schreiben:

(function () {
   "use strict";
 
   function toggleClass(el) {
      var x = el.target;
      if (x.classList.contains("show")) {
          x.classList.remove("show");
      } else {
          x.classList.add("show");
      }
   }
 
   function domReady(event) {
      var els = document.querySelectorAll(".viewportChanger"),
      i;
      for (i = 0; i &lt; els.length; ++i) {
          els[i].addEventListener("click", toggleClass); // hier schlägt JSLint bei einer anonymen Funktion Alarm
      }
 }
 
 if (document.addEventListener) {
...

Und im Vergleich eine delegate Variante des Codes:

/*global window, document*/
(function () {
    "use strict";
 
    function toggleClass(el) {
        var x = el.target;
        if (x.classList.contains("viewportChanger")) {
            if (x.classList.contains("show")) {
                x.classList.remove("show");
            } else {
                x.classList.add("show");
            }
        } else {
            window.console.log("not my business");
        }
    }
 
    function domReady(event) {
        var el = document.querySelector(".viewport");
        el.addEventListener("click", toggleClass);
    }
 
    if (document.addEventListener) {
       ...

Also auf der Haben Seite kann Vanilla JS besser von JSLint analysiert werden und Optimierungen fallen einem deutlich leichter. Auf der Soll Seite erkennt man aber auch wieder den Aufwand den zum Beispiel jQuery abfedert – im Beispiel oben gibt es noch Sonderbehandlung für den IE etc…

Fazit

Eine gute Übung ist das Programmieren kleinerer Dinge mit Vanilla JS auf jeden Fall. Außerdem kann hier JSLint direkt nachgreifen und vor großen Fehler warnen. Wenn man allerdings den jQuery Weg mit Delegation wählt, hat man skalierbaren Code der auch noch kurz und bündig ist.

Die Werte oben sind durchaus interessant. Während die Vanilla JS Verarbeitung unschlagbar schnell abgeschlossen ist, unterscheidet sich das Ergebnis von der jQuery 1.11.2 Delegation Methode nicht , bzw. ist die Ram Nnutzung sogar besser bei jQuery?! Alle anderen Ansätzen können nach dieser Messreihe getrost vergessen werden.

Aus Neugier habe ich die gleichen Tests nochmal mit 50.000 <li> Elementen durchgeführt:

jquery 1.7.2: pageLoad: 8.55 s | Script: 2.02 s | Listener: 50.002 | Ram: 46.7 MB - 83.6 MB
jquery 1.11.2: pageLoad: 8.08 s| Script: 1.50 s | Listener: 50.002 | Ram: 48.0 MB - 87.3 MB
jquery 1.11.2 & delegation: PageLoad: 6.53 s| Script: 74.871 ms| Listener: 3 | Ram: 5.0 MB - 7.1 MB
vanilla js: pageLoad: 7.29s | Script: 111.456 ms | Listener: 2 | Ram: 5.8 MB - 8.7 MB
vanilly js & delegation: PageLoad: 6.93 s | Script: 15.764 ms | Listener: 2 | Ram: 3.5 MB - 4.4 MB 

Also… Die jQuery Delegationsmethode skaliert am besten, was auch logisch ist, da nur ein Element selektiert wird. Ich müsste mein Vanilla JS also auf das gleiche System umstellen damit dieses wieder besser abschneidet…

Die Vanilla JS delegation Variante hat den geringsten Ram Bedarf und auch bei der Verarbeitungsgeschwindigkeit liegt sie erwartungsgemäß ganz vorne. Allerdings steht ihr Implementationsaufwand einem jQuery-Drei-Zeiler entgegen, der ähnlich gut performt – also doch jQuery.

One thought to “jQuery vs Vanilla JS featuring JSLint = good practice”

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.