Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Web Development
Code

Die Grundlagen für das Schreiben von hochwertigem JavaScript 

by
Difficulty:IntermediateLength:LongLanguages:

German (Deutsch) translation by Valentina (you can also view the original English article)

Zweimal im Monat besuchen wir einige der Lieblingsbeiträge unserer Leser aus der gesamten Geschichte von Nettuts+. Dieses Tutorial wurde erstmals im Oktober 2010 veröffentlicht.

Der brillante Stoyan Stefanov war in der Förderung seines Buches "JavaScript Patterns" so freundlich, dass er unseren Lesern einen Auszug aus dem Buch beisteuerte, der das Wesentliche für das Schreiben von qualitativ hochwertigem JavaScript beschreibt, z. B. Länge der Zwischenspeicherung in Schleifen nach Codierungskonventionen und mehr.

Dieser Auszug enthält auch einige Gewohnheiten, die sich nicht unbedingt auf den Code selbst beziehen, sondern mehr über den gesamten Codeerstellungsprozess, einschließlich dem Schreiben der API-Dokumentation, dem Durchführen von Peer Reviews und dem Ausführen von JSLint. Diese Gewohnheiten und bewährten Methoden können Ihnen dabei helfen, besseren, verständlicheren und wartbaren Code zu schreiben. Code, auf den Sie stolz sein können (und in der Lage sein werden), wenn Sie ihn Monate und Jahre später erneut besuchen. 


Wartbaren Code schreiben 

Softwarefehler sind teuer zu beheben. Und ihre Kosten steigen im Laufe der Zeit an, insbesondere wenn sich die Fehler in das öffentlich veröffentlichte Produkt einschleichen.  Es ist am besten, wenn Sie einen Fehler sofort beheben können, sobald Sie ihn gefunden haben. In diesem Fall ist das Problem, das Ihr Code löst, noch in Ihrem Kopf. Andernfalls gehen Sie zu anderen Aufgaben über und vergessen den gesamten Code. Die erneute Überprüfung des Codes nach einiger Zeit erfordert Folgendes: 

  • Zeit, das Problem neu zu lernen und zu verstehen 
  • Zeit, den Code zu verstehen, der das Problem lösen soll 

Ein weiteres Problem, das für größere Projekte oder Unternehmen spezifisch ist, besteht darin, dass die Person, die den Fehler schließlich behebt, nicht dieselbe Person ist, die den Fehler erstellt hat (und auch nicht dieselbe Person, die den Fehler gefunden hat). Daher ist es wichtig, die Zeit zu verkürzen, die für das Verständnis des Codes erforderlich ist. Entweder von Ihnen selbst vor einiger Zeit oder von einem anderen Entwickler im Team geschrieben.  Das ist sowohl für das Endergebnis (die Einnahmen des Unternehmens) als auch für die Zufriedenheit der Entwickler von entscheidender Bedeutung, da wir alle lieber etwas Neues und Aufregendes entwickeln würden, anstatt Stunden und Tage damit zu verbringen, alten Code zu erhalten.

Andere Tatsache im Zusammenhang mit der Softwareentwicklung im Allgemeinen ist, dass normalerweise mehr Zeit zum Lesen von Code aufgewendet wird als zum Schreiben. In Zeiten, in denen Sie konzentriert und tief in ein Problem eingebunden sind, können Sie sich hinsetzen und an einem Nachmittag eine beträchtliche Menge Code erstellen.

Der Code wird wahrscheinlich dann und dort funktionieren, aber wenn die Anwendung ausgereift ist, passieren viele andere Dinge, bei denen der Code überprüft, überarbeitet und optimiert werden muss. Zum Beispiel:

  • Fehler werden aufgedeckt.
  • Der Anwendung werden neue Funktionen hinzugefügt.
  • Die Anwendung muss in neuen Umgebungen funktionieren (z. B. erscheinen neue Browser auf dem Markt).
  • Der Code wird erneut verwendet.
  • Der Code wird komplett neu geschrieben oder in eine andere Architektur oder sogar in eine andere Sprache portiert.

Infolge der Änderungen enden die wenigen Mannstunden, die der Code geschrieben hat, zunächst in Wochen, in denen er gelesen wurde. Aus diesem Grund ist das Erstellen von wartungsfähigem Code für den Erfolg einer Anwendung von entscheidender Bedeutung.

Wartungsfähiger Code bedeutet Code, der:

  • lesbar ist 
  • konsistent ist 
  • vorhersehbar ist 
  • aussieht, als wäre es von derselben Person geschrieben worden
  • dokumentiert ist 

Minimieren non Globalen

JavaScript verwendet Funktionen zur Verwaltung des Umfangs. Eine innerhalb einer Funktion deklarierte Variable ist lokal für diese Funktion und außerhalb der Funktion nicht verfügbar.  Auf der anderen Seite sind globale Variablen diejenigen, die außerhalb einer Funktion deklariert oder einfach ohne Deklaration verwendet werden.

In jeder JavaScript-Umgebung ist ein globales Objekt verfügbar, auf das Sie zugreifen können, wenn Sie dieses außerhalb einer Funktion verwenden. Jede von Ihnen erstellte globale Variable wird zur Eigenschaft des globalen Objekts.  In Browsern gibt es zur Vereinfachung eine zusätzliche Eigenschaft des globalen Objekts namens window, die (normalerweise) auf das globale Objekt selbst verweist. Das folgende Codeausschnitt zeigt, wie eine globale Variable in einer Browserumgebung erstellt und darauf zugegriffen wird:


Das Problem mit Globalen

Das Problem mit globalen Variablen besteht darin, dass sie von dem gesamten Code in Ihrer JavaScript-Anwendung oder Webseite verwendet werden. Sie leben in demselben globalen Namensraum und es besteht immer die Möglichkeit, Kollisionen zu benennen, wenn zwei separate Teile einer Anwendung globale Variablen mit demselben Namen, aber unterschiedlichen Zwecken definieren.

In der Regel enthalten Webseiten auch Code, der nicht von den Entwicklern der Seite geschrieben wurde, beispielsweise:

  • Eine JavaScript-Bibliothek eines Drittanbieters
  • Skripte von einem Werbepartner
  • Code aus einem Benutzer-Tracking- und Analyseskript eines Drittanbieters
  • Verschiedene Arten von Widgets, Abzeichen und Schaltflächen

Angenommen, eines der Skripte von Drittanbietern definiert eine globale Variable, die beispielsweise als Ergebnis bezeichnet wird. Später definieren Sie in einer Ihrer Funktionen eine weitere globale Variable namens result.  Das Ergebnis davon ist, dass die letzte Ergebnisvariable die vorherigen überschreibt, und das Skript eines Drittanbieters funktioniert möglicherweise nicht mehr.

Daher ist es wichtig, ein guter Nachbar zu den anderen Skripts zu sein, die sich auf derselben Seite befinden, und so wenig globale Variablen wie möglich zu verwenden. Später in diesem Buch erfahren Sie mehr über Strategien zur Minimierung der Anzahl von Globals, wie z. B. das Namensraummuster oder die selbstausführenden Direktfunktionen. Das wichtigste Muster für weniger Globals ist jedoch die Verwendung von var zum Deklarieren von Variablen.

Es ist überraschend einfach, aufgrund zweier JavaScript-Funktionen unwillkürlich Globals zu erstellen. Erstens können Sie Variablen verwenden, ohne sie zu deklarieren.  Zweitens hat JavaScript den Begriff "implizierte Globals", was bedeutet, dass jede Variable, die Sie nicht deklarieren, eine Eigenschaft des globalen Objekts wird (und wie eine ordnungsgemäß deklarierte globale Variable zugänglich ist). Betrachten Sie das folgende Beispiel:

In diesem Code wird result ohne Deklaration verwendet. Der Code funktioniert gut, aber nach dem Aufruf der Funktion erhalten Sie ein weiteres variables result im globalen Namespace, der zu Problemen führen kann.

Als Faustregel gilt, Variablen immer mit var zu deklarieren, wie die verbesserte Version der Funktion sum() zeigt:

Ein anderes Anti-Muster, das implizierte Globals erstellt, besteht darin, Zuweisungen als Teil einer var-Deklaration zu verketten. In dem folgenden Snippet ist a lokal, aber b wird global, was Sie wahrscheinlich nicht beabsichtigten:

Wenn Sie sich fragen, warum dies passiert, liegt dies an der Bewertung von rechts nach links. Zuerst wird der Ausdruck b = 0 ausgewertet und in diesem Fall wird b nicht deklariert.  Der Rückgabewert dieses Ausdrucks ist 0 und wird der neuen lokalen Variablen zugewiesen, die mit var a deklariert wurde. Mit anderen Worten, es ist, als hätten Sie Folgendes eingegeben:

Wenn Sie die Variablen bereits deklariert haben, ist das Verketten von Zuweisungen in Ordnung und erstellt keine unerwarteten globalen Werte. Beispiel:

Ein weiterer Grund, globale Faktoren zu vermeiden, ist die Portabilität. Wenn Sie möchten, dass Ihr Code in verschiedenen Umgebungen (Hosts) ausgeführt wird, ist die Verwendung von Globals gefährlich, da Sie versehentlich ein Host-Objekt überschreiben können, das in Ihrer ursprünglichen Umgebung nicht vorhanden ist in einigen der anderen.


Nebenwirkungen beim Vergessen von var

Es gibt einen geringfügigen Unterschied zwischen implizierten Globals und explizit definierten - der Unterschied besteht in der Möglichkeit, diese Variablen mithilfe des Löschoperators rückgängig zu machen:

  • Mit var erstellte Globals (die außerhalb einer Funktion im Programm erstellt wurden) können nicht gelöscht werden.
  • Implizite Globals, die ohne var erstellt wurden (unabhängig davon, ob sie innerhalb von Funktionen erstellt wurden) können gelöscht werden.

Das zeigt, dass implizierte Globals technisch keine echten Variablen sind, sondern Eigenschaften des globalen Objekts. Eigenschaften können mit dem Löschoperator gelöscht werden, während Variablen nicht:

Im strikten Modus von ES5 geben Zuweisungen an nicht deklarierte Variablen (wie z. B. die beiden Antipattern im vorhergehenden Snippet) einen Fehler aus.


Zugriff auf das globale Objekt

In den Browsern ist das globale Objekt von jedem beliebigen Teil des Codes über die window-Eigenschaft zugänglich (es sei denn, Sie haben etwas Besonderes und Unerwartetes getan, beispielsweise eine lokale Variable mit dem Namen window). In anderen Umgebungen kann diese Convenience-Eigenschaft jedoch als etwas anderes bezeichnet werden (oder dem Programmierer sogar nicht zur Verfügung stehen).  Wenn Sie auf das globale Objekt zugreifen müssen, ohne das Bezeichner window fest zu codieren, können Sie auf jeder Ebene verschachtelter Funktionsbereiche Folgendes tun:

Auf diese Weise können Sie immer das globale Objekt erhalten, da Funktionen, die als Funktionen aufgerufen wurden (also nicht als Constrictors mit new), immer auf das globale Objekt zeigen sollten. Das ist in ECMAScript 5 im strikten Modus eigentlich nicht mehr der Fall. Daher müssen Sie ein anderes Muster verwenden, wenn sich Ihr Code im strikten Modus befindet.  Wenn Sie beispielsweise eine Bibliothek entwickeln, können Sie Ihren Bibliothekscode in eine unmittelbare Funktion einschließen und dann aus dem globalen Bereich einen Verweis darauf an Ihre unmittelbare Funktion als Parameter übergeben.


Einzelnes var Muster

Die Verwendung einer einzelnen var-Anweisung oben in Ihren Funktionen ist ein nützliches Muster. Es hat die folgenden Vorteile:

  • Bietet eine zentrale Stelle für die Suche nach allen lokalen Variablen, die von der Funktion benötigt werden
  • Verhindert logische Fehler, wenn eine Variable vor ihrer Definition verwendet wird
  • Erinnert Sie daran, Variablen zu deklarieren und somit Globals zu minimieren
  • Ist weniger Code (zum Tippen und Übertragen über die Leitung)

Das einzelne var-Muster sieht folgendermaßen aus:

Sie verwenden eine var-Anweisung und deklarieren mehrere durch Kommas getrennte Variablen. Es empfiehlt sich, die Variable zum Zeitpunkt der Deklaration mit einem Anfangswert zu initialisieren.  Dies kann logische Fehler verhindern (alle nicht initialisierten und deklarierten Variablen werden mit dem Wert undefined initialisiert) und die Lesbarkeit des Codes verbessern.  Wenn Sie sich den Code später ansehen, können Sie anhand des ursprünglichen Werts eine Vorstellung von der beabsichtigten Verwendung einer Variablen erhalten. War dies beispielsweise ein Objekt oder eine Ganzzahl?

Sie können zum Zeitpunkt der Deklaration auch einige tatsächliche Arbeiten ausführen, wie im Fall mit sum = a + b im vorhergehenden Code. Ein anderes Beispiel ist das Arbeiten mit DOM-Referenzen (Document Object Model).  Sie können DOM-Referenzen zusammen mit der einzelnen Deklaration lokalen Variablen zuweisen, wie der folgende Code veranschaulicht:


Hissen: Ein Problem mit verstreuten Varen

Mit JavaScript können Sie an einer beliebigen Stelle mehrere var-Anweisungen verwenden, die alle so wirken, als wären die Variablen oben in der Funktion deklariert.  Dieses Verhalten wird als Hissen bezeichnet. Das kann zu logischen Fehlern führen, wenn Sie eine Variable verwenden und diese dann in der Funktion weiter deklarieren.  Solange sich eine Variable im selben Gültigkeitsbereich (derselben Funktion) befindet, wird sie für JavaScript als deklariert angesehen, auch wenn sie vor der var-Deklaration verwendet wird. Schauen Sie sich dieses Beispiel an:

In diesem Beispiel können Sie davon ausgehen, dass die erste alert() Prompt "global" und die zweite "lokal" anzeigt. Das ist eine vernünftige Erwartung, da zum Zeitpunkt der ersten Warnung kein myname deklariert wurde und die Funktion daher verwendet werden sollte Wahrscheinlich "sehen" Sie den globalen myname.  Aber so funktioniert es nicht. Die erste Warnung sagt "undefined", da myname als lokale Variable für die Funktion betrachtet wird. (Obwohl die Deklaration danach kommt.) Alle Variablendeklarationen werden an die Spitze der Funktion angehoben.  Um diese Art von Verwirrung zu vermeiden, empfiehlt es sich daher, alle Variablen vorab zu deklarieren, die Sie verwenden möchten.

Das vorhergehende Code-Snippet verhält sich so, als ob es so implementiert wäre:

Der Vollständigkeit halber sei erwähnt, dass die Dinge auf der Implementierungsebene etwas komplexer sind. In der Codebehandlung gibt es zwei Stufen, in denen Variablen, Funktionsdeklarationen und Formalparameter in der ersten Stufe erstellt werden. Das ist die Stufe der Analyse und Eingabe des Kontexts.  In der zweiten Stufe werden die Phase der Laufzeitcodeausführung, Funktionsausdrücke und nicht qualifizierte Bezeichner (nicht deklarierte Variablen) erstellt. Aus praktischen Gründen können wir jedoch das Konzept des Hebens anwenden, das eigentlich nicht vom ECMAScript-Standard definiert wird, sondern üblicherweise zur Beschreibung des Verhaltens verwendet wird.


for Zyklen

In for-Zyklen iterieren Sie über arrays oder arrayähnliche Objekte wie arguments und HTMLCollection-Objekte. Das übliche for-Zyklus-Muster sieht folgendermaßen aus:

Ein Problem bei diesem Muster ist, dass bei jeder Schleifeniteration auf die Länge des Arrays zugegriffen wird. Das kann Ihren Code verlangsamen, insbesondere wenn myarray kein Array, sondern ein HTMLCollection-Objekt ist.

HTMLCollections sind Objekte, die von DOM-Methoden zurückgegeben werden, z.B.:

  • document.getElementsByName()
  • document.getElementsByClassName()
  • document.getElementsByTagName()

Es gibt auch eine Reihe weiterer HTMLCollections, die vor dem DOM-Standard eingeführt wurden und heute noch verwendet werden. Darunter sind u.a.:

  • document.images: Alle IMG-Elemente auf der Seitedocument.
  • document.links: Alle A-Elemente
  • document.forms: Alle Formulare
  • document.forms[0].elements: Alle Felder im ersten Formular auf der Seite

Das Problem bei Sammlungen besteht darin, dass es sich um Live-Abfragen gegen das zugrunde liegende Dokument (die HTML-Seite) handelt. Das bedeutet, dass Sie jedes Mal, wenn Sie auf length einer Sammlung zugreifen, das Live-DOM abfragen, und DOM-Vorgänge sind im Allgemeinen teuer.

Aus diesem Grund ist es besser für for-Zyklen, die Länge des Arrays (oder der Collection), das Sie durchlaufen, zwischenzuspeichern, wie im folgenden Beispiel gezeigt:

Auf diese Weise rufen Sie den Wert von Länge nur einmal ab und verwenden ihn während des gesamten Zyklus.

Das Caching der Länge beim Durchlaufen von HTMLCollections ist in allen Browsern schneller - zwischen zwei Mal schneller (Safari 3) und 190 Mal (IE7).

Wenn Sie die Auflistung im Zyklus explizit ändern möchten (z. B. durch Hinzufügen weiterer DOM-Elemente), möchten Sie wahrscheinlich, dass die Länge aktualisiert wird und nicht konstant.

Nach dem einzelnen var-Muster können Sie auch die var aus dem Zyklus nehmen und den Zyklus wie folgt erstellen:

Dieses Muster hat den Vorteil der Konsistenz, da Sie sich an das einzelne Var-Muster halten. Ein Nachteil ist, dass das Kopieren und Einfügen ganze Zyklen beim Umgestalten von Code etwas schwieriger wird.  Wenn Sie beispielsweise der Zyklusvon einer Funktion in eine andere kopieren, müssen Sie sicherstellen, dass Sie auch i und max in die neue Funktion übernehmen (und sie wahrscheinlich aus der ursprünglichen Funktion löschen, wenn sie dort nicht mehr benötigt werden).

Eine letzte Änderung der Schleife wäre, i++ durch einen der folgenden Ausdrücke zu ersetzen:

JSLint fordert Sie dazu auf. Der Grund dafür ist, dass ++ und -- "übermäßige Trickserei" fördern. Wenn Sie dem nicht zustimmen, können Sie die JSLint-Option plusplus auf false setzen. (Standardmäßig ist es wahr.)

Zwei Variationen des for-Musters führen einige Mikrooptimierungen ein, weil sie:

  • Verwenden Sie eine Variable weniger (keine max)
  • Zählen Sie auf 0 herunter, was normalerweise schneller ist, da der Vergleich mit 0 effizienter ist als die Länge des Arrays oder ein anderer Wert als 0

Das erste modifizierte Muster ist:

Und die zweite verwendet einen while-Zyklus:

Hierbei handelt es sich um Mikrooptimierungen, die nur bei leistungskritischen Vorgängen sichtbar werden. Darüber hinaus wird sich JSLint über die Verwendung von i-- beschweren.


for-in-Zyklen

for-in-Zyklen sollten verwendet werden, um Nicht-Array-Objekte zu durchlaufen. Der Schleifendurchlauf mit for-in wird auch als enumeration bezeichnet.

Technisch gesehen können Sie for-in auch zum Durchlaufen von Arrays verwenden (da in JavaScript Arrays Objekte sind), dies wird jedoch nicht empfohlen. Es kann zu logischen Fehlern führen, wenn das Array-Objekt bereits um benutzerdefinierte Funktionen erweitert wurde.  Darüber hinaus ist die Reihenfolge (die Reihenfolge) der Auflistung der Eigenschaften in einem for-in nicht garantiert. Daher ist es vorzuziehen, normal für Zyklen mit Arrays und for-in-Zyklen für Objekte zu verwenden.

Es ist wichtig, die Methode hasOwnProperty() zu verwenden, wenn Objekteigenschaften durchlaufen werden, um Eigenschaften herauszufiltern, die in der Prototypkette vorkommen.

Betrachten Sie das folgende Beispiel:

In diesem Beispiel haben wir ein einfaches Objekt namens man, das mit einem Objektliteral definiert ist. Vor oder nach der Definition von man wurde der Objektprototyp um eine nützliche Methode namens clone() erweitert.  Die Prototypkette ist live, dh alle Objekte erhalten automatisch Zugriff auf die neue Methode. Um zu vermeiden, dass die Methode clone() beim Auflisten von man angezeigt wird, müssen Sie hasOwnProperty() aufrufen, um die Prototypeneigenschaften herauszufiltern.  Wenn Sie die Filterung nicht durchführen, kann die Funktion clone() angezeigt werden. Das ist in den meisten Fällen ein unerwünschtes Verhalten:

Ein anderes Muster für die Verwendung von hasOwnProperty() ist das Aufrufen dieser Methode aus dem Object.prototyp, wie folgt:

Der Vorteil ist, dass Sie Namenskollisionen vermeiden können, wenn das man-Objekt hasOwnProperty neu definiert hat.Um die langen Eigenschaftssuchen bis hin zu Object zu vermeiden, können Sie eine lokale Variable verwenden, um das Objekt zu "cache":

Streng genommen ist die Verwendung von hasOwnProperty() kein Fehler. Je nach Aufgabe und Vertrauen in den Code können Sie ihn überspringen und die Zyklen etwas beschleunigen.  Wenn Sie sich jedoch nicht sicher sind, welchen Inhalt das Objekt (und seine Prototypkette) haben, sind Sie sicherer, wenn Sie die hasOwnProperty()-Prüfung hinzufügen.

Eine Formatierungsvariation (die JSLint nicht bestanden hat) überspringt eine geschweifte Klammer und setzt das if in dieselbe Zeile.  Der Vorteil ist, dass die Schleifenanweisung eher wie ein vollständiger Gedanke gelesen wird („für jedes Element, das eine eigene Eigenschaft X hat, etwas mit X tun“). Außerdem gibt es weniger Einrückungen, bevor Sie den Hauptzweck des Zyklus erreichen:


(Nicht) Erweiterte Prototypen erweitern

Das Erweitern der Prototypeneigenschaft von Konstruktorfunktionen ist eine leistungsfähige Möglichkeit zum Hinzufügen von Funktionen, kann jedoch manchmal zu leistungsfähig sein.

Es ist verlockend, Prototypen eingebauter Konstruktoren wie Object(), Array() oder Function() zu erweitern, kann jedoch die Wartbarkeit ernsthaft beeinträchtigen, da dies den Code weniger vorhersehbar macht. Andere Entwickler, die Ihren Code verwenden, erwarten wahrscheinlich, dass die integrierten JavaScript-Methoden konsistent funktionieren, und erwarten keine Erweiterungen.

Außerdem können Eigenschaften, die Sie dem Prototypen hinzufügen, in Schleifen angezeigt werden, die hasOwnProperty() nicht verwenden, sodass sie zu Verwirrung führen können.

Daher ist es am besten, wenn Sie keine integrierten Prototypen erweitern. Sie können nur dann eine Ausnahme von der Regel machen, wenn alle diese Bedingungen erfüllt sind:

  • Es wird erwartet, dass zukünftige ECMAScript-Versionen oder JavaScript-Implementierungen diese Funktionalität als integrierte Methode konsistent implementieren.  Sie können beispielsweise in ECMAScript 5 beschriebene Methoden hinzufügen, während Sie darauf warten, dass die Browser aufholen. In diesem Fall definieren Sie nur die nützlichen Methoden im Voraus.
  • Sie prüfen, ob Ihre benutzerdefinierte Eigenschaft oder Methode nicht bereits vorhanden ist. Möglicherweise ist sie bereits an anderer Stelle im Code implementiert oder bereits Teil der JavaScript-Engine eines der unterstützten Browser.
  • Sie dokumentieren die Kommunikation und kommunizieren sie mit dem Team.

Wenn diese drei Bedingungen erfüllt sind, können Sie mit dem benutzerdefinierten Zusatz zum Prototyp nach folgendem Muster fortfahren:


Muster wechseln

Sie können die Lesbarkeit und Robustheit Ihrer switch-Anweisungen verbessern, indem Sie diesem Muster folgen:

Die Stilkonventionen in diesem einfachen Beispiel sind:

  • Ausrichten jedes case mit switch (eine Ausnahme von der Einrückregel für geschweifte Klammern).
  • Einrücken des Codes in jedem Fall.
  • Beenden jedes case mit einer klaren break;.
  • Vermeiden Sie Durchfälle (wenn Sie den Durchbruch absichtlich weglassen). Wenn Sie absolut überzeugt sind, dass ein Durchbruch der beste Ansatz ist, sollten Sie solche Fälle dokumentieren, da sie für die Leser Ihres Codes wie Fehler aussehen könnten.
  • Beenden switch mit default:, um sicherzustellen, dass immer ein vernünftiges Ergebnis vorliegt, auch wenn keiner der Fälle übereinstimmt.

Impliziertes Typecasting vermeiden

JavaScript konvertiert implizite Variablen, wenn Sie sie vergleichen. Deshalb geben Vergleiche wie false == 0 oder "" == 0 true zurück.

Verwenden Sie immer die Operatoren === und !==, die sowohl die Werte als auch den Typ der verglichenen Ausdrücke prüfen, um Verwirrung durch die implizierte Typumwandlung zu vermeiden.

Es gibt eine andere Denkschule, die die Ansicht vertritt, dass es überflüssig ist, === zu verwenden, wenn == ausreicht. Wenn Sie beispielsweise typeof verwenden, wird eine Zeichenfolge zurückgegeben. Es gibt also keinen Grund, strikte Gleichheit zu verwenden.  JSLint erfordert jedoch strikte Gleichheit. Dadurch wirkt der Code konsistent und verringert den mentalen Aufwand beim Lesen von Code. ("Ist das == absichtlich oder eine Auslassung?")


Vermeiden eval()

Wenn Sie die Verwendung von eval() in Ihrem Code erkennen, denken Sie daran, dass das Mantra „eval() is evil“ ist. Diese Funktion nimmt eine beliebige Zeichenfolge und führt sie als JavaScript-Code aus.  Wenn der betreffende Code im Voraus bekannt ist (nicht zur Laufzeit bestimmt), gibt es keinen Grund, eval() zu verwenden.  Wenn der Code zur Laufzeit dynamisch generiert wird, ist es oft besser, das Ziel ohne eval() zu erreichen. Zum Beispiel ist die Verwendung der Notation in eckigen Klammern für den Zugriff auf dynamische Eigenschaften besser und einfacher:

Die Verwendung von eval() hat auch Auswirkungen auf die Sicherheit, da Sie möglicherweise Code ausführen (der beispielsweise aus dem Netzwerk stammt), der manipuliert wurde. Das ist ein übliches Musterverfahren, wenn eine JSON-Antwort aus einer Ajax-Anforderung verarbeitet wird.  In diesen Fällen ist es besser, die integrierten Methoden des Browsers zu verwenden, um die JSON-Antwort zu parsen, um sicherzustellen, dass sie sicher und gültig ist. Für Browser, die JSON.parse() nicht nativ unterstützen, können Sie eine Bibliothek von JSON.org verwenden.

Beachten Sie auch, dass die Übergabe von Strings an setInterval(), setTimeout() und den Function()-Konstruktor weitgehend der Verwendung von eval() ähnelt und daher vermieden werden sollte. Hinter den Kulissen muss JavaScript die Zeichenfolge, die Sie als Programmcode übergeben, noch auswerten und ausführen:

Die Verwendung des neuen Function()-Konstruktors ähnelt eval() und sollte mit Vorsicht angegangen werden. Es könnte ein mächtiges Konstrukt sein, wird aber oft missbraucht. Wenn Sie eval() unbedingt verwenden müssen, können Sie stattdessen die neue Funktion() verwenden.  Es gibt einen kleinen potenziellen Vorteil, da der in new Function() ausgewertete Code in einem lokalen Funktionsumfang ausgeführt wird. Daher werden alle Variablen, die mit var im auszuwertenden Code definiert wurden, nicht automatisch zu globalen Variablen.  Eine weitere Möglichkeit, automatische Globals zu verhindern, besteht darin, den eval()-Aufruf in eine unmittelbare Funktion zu integrieren.

Betrachten Sie das folgende Beispiel. Hier bleibt nur un als globale Variable, die den Namespace verschmutzt:

Ein weiterer Unterschied zwischen eval() und dem Funktionskonstruktor besteht darin, dass eval() die Scope-Chain beeinflussen kann, während Function weitaus mehr Sandkasten hat. Unabhängig davon, wo Sie Function ausführen, wird nur der globale Gültigkeitsbereich angezeigt.  Es kann also weniger lokale variable Verschmutzung bewirken. Im folgenden Beispiel kann eval() auf eine Variable in ihrem äußeren Gültigkeitsbereich zugreifen und diese ändern, während Function nicht möglich ist (beachten Sie auch, dass die Verwendung von Function oder new Function identisch ist):


Anzahl Konvertierungen mit parseInt()

Mit parseInt() können Sie einen numerischen Wert aus einer Zeichenfolge abrufen. Die Funktion akzeptiert einen zweiten Radix-Parameter, der häufig weggelassen wird, aber nicht sein sollte.  Die Probleme treten auf, wenn die zu analysierende Zeichenfolge mit 0 beginnt: beispielsweise ein Teil eines Datums, das in ein Formularfeld eingegeben wird. Zeichenfolgen, die mit 0 beginnen, werden in ECMAScript 3 als Oktalzahlen (Basis 8) behandelt. Das hat sich jedoch in ES5 geändert.  Geben Sie immer den Parameter radix an, um Inkonsistenzen und unerwartete Ergebnisse zu vermeiden:

Wenn Sie in diesem Beispiel den Parameter radix wie parseInt (year) weglassen, ist der zurückgegebene Wert 0, da für "09" die Oktalzahl angenommen wird (als ob Sie parseInt (year, 8) eingegeben haben) und 09 keine gültige Ziffer in ist Basis 8.

Alternative Methoden zum Konvertieren einer Zeichenfolge in eine Zahl sind:

Diese sind oft schneller als parseInt(), da parseInt(), wie der Name vermuten lässt, analysiert und nicht einfach konvertiert. Wenn Sie jedoch Eingaben wie „08 Hallo“ erwarten, gibt parseInt() eine Zahl zurück, während die anderen mit NaN fehlschlagen.


Kodierungskonventionen

Es ist wichtig, Kodierungskonventionen festzulegen und zu befolgen - sie machen Ihren Code konsistent, vorhersehbar und lassen sich viel einfacher lesen und verstehen. Ein neuer Entwickler, der dem Team beitritt, kann die Konventionen durchlesen und ist viel früher produktiver, da er den Code versteht, der von jedem anderen Teammitglied geschrieben wurde.

Viele Flamewars wurden in Meetings und auf Mailinglisten über bestimmte Aspekte bestimmter Codierungskonventionen (z. B. Codeeinzug - Tabs oder Leerzeichen?) Gekämpft. Wenn Sie also die Annahme von Konventionen in Ihrer Organisation vorschlagen, sollten Sie sich auf Widerstand einstellen und andere, aber ebenso starke Meinungen hören.  Denken Sie daran, dass es viel wichtiger ist, Konventionen oder Konventionen festzulegen und konsequent zu befolgen, als die genauen Einzelheiten dieser Konventionen.


Vertiefung

Code ohne Einrückung ist nicht lesbar. Das einzige, was noch schlimmer ist, ist Code mit inkonsistenter Einrückung, da er aussieht, als würde er einer Konvention folgen, aber es kann auf dem Weg verwirrende Überraschungen geben.  Es ist wichtig, die Einrückung zu standardisieren.

Einige Entwickler bevorzugen die Einrückung mit Registerkarten, da jeder seinen Editor so anpassen kann, dass die Registerkarten mit der individuell bevorzugten Anzahl von Leerzeichen angezeigt werden. Einige bevorzugen Leerzeichen - normalerweise vier.  Es spielt keine Rolle, solange jeder im Team denselben Konventionen folgt. In diesem Buch wird zum Beispiel der Einzug mit vier Leerzeichen verwendet, der auch in JSLint die Standardeinstellung ist.

Und was soll man einrücken? Die Regel ist einfach - alles in geschweiften Klammern.  Dies bedeutet die Körper von Funktionen, Schleifen (do, while, for, for-in), ifs, switches und object-Eigenschaften in der object-Liternotation. Der folgende Code zeigt einige Beispiele für die Verwendung von Einrückungen:


Geschweifte Klammern

Geschweifte Klammern sollten immer verwendet werden, auch wenn sie optional sind.  Wenn Sie nur eine Aussage in einem if oder einem for haben, sind geschweifte Klammern nicht erforderlich. Sie sollten sie jedoch immer verwenden. Dadurch wird der Code konsistenter und einfacher zu aktualisieren.

Stellen Sie sich vor, Sie haben eine for-Schleife mit nur einer Anweisung. Sie könnten die geschweiften Klammern weglassen und es wird kein Syntaxfehler auftreten:

Was aber, wenn Sie später eine weitere Zeile in den Loop einfügen?

Die zweite Warnung befindet sich außerhalb der Schleife, obwohl Sie durch die Einrückung möglicherweise betrogen werden. Auf lange Sicht sollten Sie immer die geschweiften Klammern verwenden, auch für einzeilige Blöcke:

Ähnlich für if-Bedingungen:


Position der Klammer öffnen

Entwickler neigen auch dazu, Präferenzen zu haben, wo sich die öffnende geschweifte Klammer befinden sollte - in derselben Zeile oder in der folgenden Zeile?

ODER:

In diesem speziellen Beispiel ist es eine Frage der Präferenz, aber es gibt Fälle, in denen sich das Programm je nach Position der Klammer anders verhält. Das ist auf den semicolon insertion mechanism zurückzuführen. JavaScript ist nicht wählerisch, wenn Sie Ihre Zeilen nicht ordnungsgemäß mit einem Semikolon beenden und für Sie hinzufügen. Dieses Verhalten kann zu Problemen führen, wenn eine Funktion ein Objektliteral zurückgibt und sich die öffnende Klammer in der nächsten Zeile befindet:

Wenn Sie davon ausgehen, dass diese Funktion ein Objekt mit einer name-Eigenschaft zurückgibt, werden Sie überrascht sein. Aufgrund der implizierten Semikolons gibt die Funktion undefined zurück. Der vorhergehende Code entspricht diesem Code:

Verwenden Sie zum Schluss immer geschweifte Klammern und setzen Sie die öffnende immer in die gleiche Zeile wie die vorherige Anweisung:

Ein Hinweis zu Semikolons: Wie bei den geschweiften Klammern sollten Sie immer Semikolons verwenden, auch wenn sie von den JavaScript-Parsern impliziert werden. Dies fördert nicht nur Disziplin und eine strengere Herangehensweise an den Code, sondern hilft auch, Mehrdeutigkeiten zu beseitigen, wie das vorige Beispiel gezeigt hat.


Leerraum

Die Verwendung von Leerzeichen kann auch zu einer besseren Lesbarkeit und Konsistenz des Codes beitragen. In geschriebenen englischen Sätzen verwenden Sie Intervalle nach Kommas und Punkten.  In JavaScript folgen Sie der gleichen Logik und fügen Intervalle nach auflistenartigen Ausdrücken (entspricht Kommas) und End-of-Anweisungen (entsprechend einem "Gedanken") hinzu.

Gute Orte, um ein weißes Feld zu nutzen, sind:

  • Nach den Semikolons, die die Teile einer for-Zyklus trennen: Zum Beispiel for (var i  = 0; i
  • Initialisieren mehrerer Variablen (i und max) in einer for-Schleife: for (var i = 0, max = 10; i
  • Nach den Kommas, die Arrayelemente begrenzen: var a = [1, 2, 3];
  • Nach Kommas in Objekteigenschaften und nach Doppelpunkten, die Eigenschaftsnamen und 
    teilen ihre Werte: var o = {a: 1, b: 2};
  • Funktionsargumente abgrenzen: myFunc(a, b, c)
  • Vor den geschweiften Klammern in Funktionsdeklarationen: function myFunc() {}
  • Nach function in anonymen Funktionsausdrücken: var myFunc = function () {};

Eine weitere gute Verwendung für Leerzeichen besteht darin, alle Operatoren und ihre Operanden durch Leerzeichen zu trennen, was im Wesentlichen ein Leerzeichen vor und nach +, -, *, =,, = ===,! ==, &&, ||, + =, und so weiter:

Und noch eine letzte Anmerkung zum Leerraum - geschweiften Klammern. Es ist gut, ein Leerzeichen zu verwenden:

  • Bevor Sie geschweifte Klammern ({) in Funktionen, if-else-Fällen, Zyklen und Objektliteralen öffnen
  • Zwischen der schließenden geschweiften Klammer (}) und else oder while

Ein Argument gegen die liberale Nutzung von Leerzeichen könnte die Erhöhung der Dateigröße sein, aber
Minification kümmert sich um dieses Problem.

Ein oft übersehener Aspekt der Lesbarkeit von Code ist die Verwendung von vertikalem Leerraum. Sie können leere Zeilen verwenden, um Codeeinheiten zu trennen, genau wie Absätze in der Literatur verwendet werden, um Ideen zu trennen.


Regeln der Namensgebung

Eine andere Möglichkeit, Ihren Code vorhersehbarer und wartbarer zu machen, ist die Verwendung von Namenskonventionen. Das bedeutet, dass Sie Namen für Ihre Variablen und Funktionen auf konsistente Weise auswählen.

Im Folgenden finden Sie einige Vorschläge für Namenskonventionen, die Sie nach Belieben übernehmen oder anpassen können. Eine Konvention zu haben und ihr konsequent zu folgen, ist viel wichtiger als das, was diese Konvention eigentlich ist.


Konstruktoren mit Kapital ausstatten

JavaScript hat keine Klassen, aber Konstruktorfunktionen werden mit new aufgerufen:

Da Konstruktoren immer noch nur Funktionen sind, ist es hilfreich, wenn Sie anhand eines Funktionsnamens erkennen können, ob er sich als Konstruktor oder als normale Funktion verhalten soll.

Wenn Sie Konstruktoren mit einem großen Anfangsbuchstaben benennen, erhalten Sie diesen Hinweis. Die Verwendung von Kleinbuchstaben für Funktionen und Methoden bedeutet, dass sie nicht mit new aufgerufen werden sollten:


Wörter trennen

Wenn Sie mehrere Wörter in einer Variablen oder einem Funktionsnamen haben, ist es eine gute Idee, einer Konvention zu folgen, wie die Wörter getrennt werden.  Eine übliche Konvention ist die Verwendung des sogenannten Kamelgehäuses. Nach der Kamelfallkonvention geben Sie die Wörter in Kleinbuchstaben ein, wobei nur der erste Buchstabe jedes Wortes großgeschrieben wird.

Für Ihre Konstruktoren können Sie wie in MyConstructor() den oberen Kamelfall verwenden, und für Funktions- und Methodennamen können Sie den unteren Kamelfall verwenden, wie in myFunction(), calculateArea() und getFirstName().

Und was ist mit Variablen, die nicht funktionieren? Entwickler verwenden im Allgemeinen für die Namen von Variablen ein kleines Kamelfeld. Eine andere gute Idee ist die Verwendung von Kleinbuchstaben, die durch einen Unterstrich getrennt sind: z. B. first_name, favorite_bands und old_company_name.  Mit dieser Notation können Sie visuell zwischen Funktionen und allen anderen Bezeichnern - Grundelementen und Objekten - unterscheiden.

ECMAScript verwendet für beide Methoden und Eigenschaften die Groß- und Kleinschreibung von Kamelen, obwohl die Namen der Mehrfachworte selten sind (lastIndex- und ignoreCase-Eigenschaften von regulären Ausdrucksobjekten).


Andere Benennungsmuster

Manchmal verwenden Entwickler eine Namenskonvention, um Sprachfunktionen zu erstellen oder zu ersetzen.

Zum Beispiel gibt es keine Möglichkeit, Konstanten in JavaScript zu definieren (obwohl einige eingebettet sind, wie z. B. Number.MAX_VALUE). Daher haben die Entwickler die Konvention der Verwendung von All-Caps für die Benennung von Variablen übernommen, deren Werte sich während der Lebensdauer nicht ändern sollten des Programms wie:

Es gibt eine andere Konvention, die um die Verwendung aller Obergrenzen konkurriert: Verwenden von Großbuchstaben für Namen von globalen Variablen. Die Benennung von Globals mit allen Caps kann die Praxis der Minimierung ihrer Anzahl verstärken und sie leicht unterscheidbar machen.

Ein anderer Fall der Verwendung einer Konvention zum Nachahmen von Funktionalität ist die Private Members Convention.  Obwohl Sie echte Vertraulichkeit in JavaScript implementieren können, ist es für Entwickler manchmal einfacher, ein Unterstrich-Präfix zu verwenden, um eine private Methode oder Eigenschaft zu kennzeichnen. Betrachten Sie das folgende Beispiel:

In diesem Beispiel soll getName() eine öffentliche Methode sein, Teil der stabilen API, wohingegen _getFirst() und _getLast() privat sein sollen. Dies sind immer noch normale öffentliche Methoden. Wenn Sie jedoch das Unterstrich-Präfix verwenden, werden die Benutzer des Personenobjekts darauf hingewiesen, dass diese Methoden nicht garantiert in der nächsten Version funktionieren und nicht direkt verwendet werden sollten.  Beachten Sie, dass sich JSLint über die Unterstrich-Präfixe beschwert, es sei denn, Sie setzen die Option nomen: false.

Nachfolgend einige Varianten der _private-Konvention:

  • Verwenden eines nachgestellten Unterstrichs für privat, wie in name_ und getElements_()
  • Verwenden Sie ein Unterstrich-Präfix für _protected Eigenschaften und zwei für __private-Eigenschaften
  • In Firefox sind einige interne Eigenschaften verfügbar, die technisch nicht Teil der Sprache sind, und sie werden mit einem Präfix mit zwei Unterstrichen und einem Suffix mit zwei Unterstrichen bezeichnet, wie __proto__ und __parent__.

Kommentare schreiben

Sie müssen Ihren Code kommentieren, auch wenn es unwahrscheinlich ist, dass eine andere Person als Sie ihn berührt. Wenn Sie sich in ein Problem vertiefen, denken Sie oft, dass es offensichtlich ist, was der Code bewirkt, aber wenn Sie nach einer Woche wieder zum Code zurückkehren, fällt es Ihnen schwer, sich daran zu erinnern, wie es genau funktioniert hat.

Sie sollten das Offensichtliche nicht über Bord gehen lassen: jede einzelne Variable oder jede einzelne Zeile. In der Regel müssen Sie jedoch alle Funktionen, ihre Argumente und Rückgabewerte sowie alle interessanten oder ungewöhnlichen Algorithmen oder Techniken dokumentieren.  Betrachten Sie die Kommentare als Hinweise für zukünftige Leser des Codes. Die Leser müssen verstehen, was Ihr Code tut, ohne viel mehr zu lesen als nur die Kommentare und die Funktions- und Eigenschaftsnamen.  Wenn Sie beispielsweise fünf oder sechs Codezeilen für eine bestimmte Aufgabe haben, kann der Leser die Codedetails überspringen, wenn Sie eine einzeilige Beschreibung angeben, die den Zweck des Codes und den Grund dafür beschreibt.  Es gibt keine festen Regeln oder ein Verhältnis von Kommentaren zu Code. Für einige Teile des Codes (etwa reguläre Ausdrücke) sind möglicherweise mehr Kommentare als Code erforderlich.

Die wichtigste Gewohnheit, die jedoch am schwersten zu befolgen ist, ist, die Kommentare auf dem neuesten Stand zu halten, da veraltete Kommentare irreführen können und viel schlimmer sind als überhaupt keine Kommentare.


Über den Autor

Stoyan Stefanov ist ein Yahoo! Webentwickler und Autor, Mitwirkender und technischer Prüfer verschiedener O'Reilly-Bücher. Er spricht regelmäßig auf Konferenzen und in seinem Blog unter www.phpied.com über Themen zur Webentwicklung.  Stoyan ist der Schöpfer des Bildoptimierungstools smush.it, YUI-Mitarbeiter und Architekt des Performance-Optimierungstools YSlow 2.0 von Yahoo.


Kaufen Sie das Buch


JavaScript Patterns

Dieser Artikel ist ein Auszug aus "JavaScript Patterns" von O'Reilly Media.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.