Impressum

Markus Nix
JavaScript Performance
schnell+kompakt
ISBN: 978-3-86802-611-5

© 2012 entwickler.press

ein Imprint der Software & Support Media GmbH
http://www.entwickler-press.de
http://www.software-support.biz

Ihr Kontakt zum Verlag und Lektorat: lektorat@entwickler-press.de

Bibliografische Information Der Deutschen Bibliothek
Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.ddb.de abrufbar.

Lektorat: Sebastian Burkart
Korrektorat: Katharina Klassen, Frauke Pesch
Satz: Laura Acker
Umschlaggestaltung: Maria Rudi

Alle Rechte, auch für Übersetzungen, sind vorbehalten. Reproduktion jeglicher Art (Fotokopie, Nachdruck, Mikrofilm, Erfassung auf elektronischen Datenträgern oder andere Verfahren) nur mit schriftlicher Genehmigung des Verlags. Jegliche Haftung für die Richtigkeit des gesamten Werks, kann, trotz sorgfältiger Prüfung durch Autor und Verlag, nicht übernommen werden. Die im Buch genannten Produkte, Warenzeichen und Firmennamen sind in der Regel durch deren Inhaber geschützt.

Speedy Gonzales in tiefer Ehrfurcht gewidmet

Vorwort

JavaScript eilt der Ruf voraus, die „am meisten missverstandene Programmiersprache der Welt“ zu sein – glaubt man Douglas Crockford. Häufig als Spielzeug verspottet, liegen doch hinter der trügerischen Einfachheit mächtige Sprachfeatures. Das Jahr 2005 hat die Geburt einer Reihe mächtiger JavaScript-Bibliotheken gesehen, allen voran jQuery und Prototype – elaborierte Bibliotheken, die eindrücklich vor Augen geführt haben, dass eine tiefere Kenntnis von JavaScript für jeden ambitionierten Webentwickler Pflicht ist. Doch dieser große Entwicklungssprung hatte auch Schattenseiten: er hat viele Entwickler davon abgehalten, ein tieferes Verständnis von JavaScript zu entwickeln. Wenn Sie jemals jQuerys each-Funktion verwendet haben, um über ein kurzes Array zu iterieren, dann haben Sie eine ungefähre Ahnung, was ich meine. Nun kann man es diesen populären Bibliotheken schwerlich ankreiden, dass sie es uns so einfach machen. Aber man kann sich vergegenwärtigen, dass man für diese Bequemlichkeit einen Preis bezahlen muss. Und die Währung heißt Geschwindigkeit.

Im Gegensatz zu den meisten Programmiersprachen kennt JavaScript kein Eingabe-/Ausgabe-Konzept. Die Stärken von JavaScript liegen darin, als interpretierte Sprache innerhalb eines Hosts zu existieren. Und es obliegt eben jenem Host Environment (typischerweise einem Browser), entsprechende Mechanismen bereitzustellen, um mit der Außenwelt in Kontakt zu treten. JavaScript-Interpreter stecken in vielen bekannten Programmen, z.B. Adobe Acrobat, Photoshop oder Apple Dashboard Widgets, und sind auf Smartphones ebenso zu Hause wie auf Webservern, Node.js sei Dank. Plattformen wie Adobe Air oder Titanium Appcelerator bauen gleich gänzlich auf JavaScript auf.

JavaScript ist eine objektorientierte dynamische Sprache mit Typen und Operatoren, einer Handvoll nativer Objekte und Methoden. In JavaScript ist jeder Datentyp eigentlich ein Objekt. Ein grundlegender Unterschied zu anderen objektorientierten Sprachen besteht darin, dass JavaScript kein Klassenkonzept kennt, aber einen vergleichbaren Mechanismus auf der Grundlage von Objektprototypen, der es z.B. gestattet, ein Objekt zur Laufzeit zu erweitern. Ein weiterer irritierender Unterschied: Auch Funktionen sind in JavaScript letztlich Objekte, die es gestatten, ausführbaren Code an andere Objekte weiterzureichen. Ich vermute, dass Sie sich über diese grundlegenden Unterschiede im Klaren sind. Denn dies ist keine Einführung, es ist vielmehr eine Sammlung von Performancetipps und Best Practices, gesammelt über einen Zeitraum von fast fünfzehn Jahren.

Auch wenn die Sprache selbst in der letzten Dekade keine wirklich grundlegenden Veränderungen erfahren hat, so ist doch unter Entwicklern eine ständige Evolution im Gange. Ein permanentes Streben nach größtmöglicher Eleganz und Effizienz, ein beharrliches Ringen um den besten Lösungsweg. Im Grunde programmieren wir seit geraumer Zeit schwergewichtige Applikationen mit eben jener Sprache, mit der wir zuvor lediglich lustige Texteffekte in die Statusleiste des Browsers gezaubert haben. Und mit diesem Hintergrund neue Herausforderungen zu meistern ist, als würde man mit einem Dacia an einem Formel-1-Rennen teilnehmen oder als wäre man in der Vergangenheit mit einem Formel-1-Wagen gefahren, allerdings nur im ersten Gang. Erst das gesteigerte Interesse vieler Entwickler an JavaScript führt nun dazu, dass wir zunehmend das Potenzial von JavaScript ausloten. Nennen wir es Hype oder Web 2.0 – erst diese künstlichen Meilensteine im diffusen Feld der Webentwicklung erzeugten breite Aufmerksamkeit für eine Sprache, die auch schon vor zehn Jahren ähnliche Mechanismen bereitgestellt hat. Kennen Sie noch DHTML? Oder Netscapes layer?

Um im Bild zu bleiben: Mit JavaScript konnte man schon immer 320 km/h fahren. Nur, dass es jetzt jeder weiß. Was nicht zwangsläufig bedeutet, dass wir deshalb alle gleich erstklassige Formel-1-Piloten wären. Schnelle Prozessoren gaukeln uns vor, dass wir schnelle Programme hätten. Das mag bei kleinen Programmen funktionieren, mit größeren fliegen wir aus der Kurve. Wir brauchen Training und ein tieferes Verständnis der Sprache, um skalierbare Applikationen programmieren zu können. Performance war vernachlässigbar, als es nur darum ging, skriptgesteuert die Hintergrundfarbe eines HTML-Dokuments zu verändern. Jetzt – speziell im Hinblick auf DOM-Manipulationen und die neuen Möglichkeiten von HTML5 – ist die Frage nach effizientem Code nicht länger unter den Teppich zu kehren. Nicht eval is evil, sondern Unkenntnis! Snappy, not sloppy – das ist die Attitüde, die moderne Echzeitapplikationen überhaupt erst möglich macht.

Bestimmte Best Practices zu ignorieren bedeutet, dass der Browser unnötige Arbeit leisten muss. Dabei darf es uns nicht genügen, einfach nur zu sagen, dass „X schneller ist als Y“, es bedarf eines Belegs. Wo immer sinnvoll, wird ein Aspekt der Performanceoptimierung durch einem Link zu einem jsPerf-Testcase (http://jsperf.com/) ergänzt. Diese hilfreiche Website gestattet, die Performance kleiner Code-Snippets zu vergleichen und diese Tests der Allgemeinheit zur Verfügung zu stellen. Und die „Allgemeinheit“ reichert die Informationen durch die Messergebnisse des spezifischen Browsers an, in dem der Testcase lief, z.B. hier:
http:// jsperf.com/jquery-tree-traversing. jsPerf-Tests werden wiederholt ausgeführt, und zwar mindestens so oft wie es nötig ist, um ein aussagekräftiges Ergebnis abzuleiten. Je mehr Operationen pro Sekunde, desto besser.

Es geht natürlich auch unkomplizierter. Die Firefox-Erweiterung Firebug ist z.B. eine gute Wahl, um sich rasch Klarheit über vermeintliche Flaschenhälse zu verschaffen. Nicht unbedingt nach den Kriterien absoluter Exaktheit, aber in jedem Fall zielführend. Hier ein kurzes Beispiel zum Verständnis des Timer-Features:

function trackSomething() {
  console.time(„MyTimer“);

  for (var x = 5000; x > 0; x--) {
    // do something
  }

  console.timeEnd(„MyTimer“);
};

Und nicht zu vergessen die interaktive Konsole, vor allem bei der Entwicklung effizienter Selektoren. Oder JSLitmus (http://www.broofa.com/Tools/JSLitmus/). Also, keine Entschuldigungen!

Performanceoptimierung ist das Herz fast aller programmiertechnischen Bestrebungen, genauer: die Beugung gegenwärtiger technischer Beschränkungen. Das Biest ist gerissen genug, von Zeit zu Zeit seinen Namen zu ändern, aber es ist stets allgegenwärtig. Einst hieß es Prozessorgeschwindigkeit, dann Bandbreitenlimitierung, gegenwärtig Hardwarebeschleunigung für mobile Endgeräte. Oder Speicherplatz oder... Sie sind ihm sicher schon begegnet! Das ist nicht weiter schlimm, solange Sie Beschränkungen als den geheimen Ursprung von Kreativität betrachten. Es sind eben diese Beschränkungen, die einen Schlüsselfaktor für die rasante Entwicklung im Web Development darstellen. Haben Sie vor fünf Jahren über die Geschwindigkeit Ihres Handyprozessors nachgedacht? Ich nicht!

Die JavaScript-Welt fragmentarisiert sich zunehmend. An die Stelle monolithischer Bibliotheken treten zunehmend kleinere, leichtgewichtigere Bibliotheken, deren geglückte Kombination auf die Qualität Ihrer Arbeit maßgeblichen Einfluss hat. Zeit, den Blick unter die Haube zu wagen. Zeit, die richtigen Fragen zu stellen. Zeit, den eigenen Code zu optimieren. Und auf keinen Fall wahllos, sondern dort, wo mit kleinen Veränderungen große Verbesserungen erreicht werden können. Don't tune for quirks. Vermeiden Sie Verbesserungen mit kurzer Halbwertszeit, denn die Browserwelt ist stetigem Wandel unterworfen.

Gerade die Beschränkungen mobiler Plattformen mit geringerer Ausführungsgeschwindigkeit und höherer Latenz zwingen uns dazu, unsere Codebasis schmal und leichtgewichtig zu gestalten, gerade so wie vor ein paar Jahren, als Bandbreite der alles determinierende Faktor der Webentwicklung war. Sich daran zu erinnern, kann ein guter Ausgangspunkt sein. Überdenken Sie Ihre Entscheidungen von Zeit zu Zeit, fragen Sie sich kritisch, ob diese oder jene Bibliothek unbedingt benötigt wird, ob das gewählte Framework angemessen, der Overhead gerechtfertig ist. Ob es nicht vielleicht auch einfacher ginge. Weniger Code ist in den meisten Fällen auch gleichbedeutend mit effizienterem Code. Weniger Code bedeutet mehr Flexibilität und weniger Abhängigkeit. Leichtes Gepäck, Ninja-Style!

Markus Nix

Potsdam, im Sommer 2012

1 Grundlagen

1.1 Variablen

Beginnen wir mit einer Hauptzutat des Programmierens, der Variablen. Viele Programmiersprachen verlangen vor der ersten Verwendung eine explizite Deklaration einer Variablen, JavaScript hingegen nicht. Das bedeutet aber nicht, dass Sie es nicht tun sollten. Denn wenn in JavaScript eine Variable nicht explizit mit dem Schlüsselwort deklariert wird, gilt sie als global (oder vielleicht dem nächsthöheren Funktionskontext zugehörig?) und das bremst Ihren Code. Warum? Weil der Interpreter sich nun auf die Suche machen muss, wo die Variable ursprünglich deklariert wurde. Nehmen wir das folgende Beispiel:

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!