Schlüsselprinzipien für wartbares JavaScript

JavaScript ist eine merkwürdige Sprache. Es ist leicht zu schreiben, aber schwer zu meistern. Am Ende dieses Artikels verwandeln Sie hoffentlich Ihren Spaghetti-Code in ein Fünf-Gänge-Menü, das voll lesbarer, haltbarer Leckereien ist!


Warum ist es so hart??

Vor allem beim Schreiben von JS-Code ist zu beachten, dass es sich um eine dynamische Sprache handelt. Das heißt, es gibt viel von Möglichkeiten, Dinge zu tun. Sie müssen sich nicht mit stark typisierten Klassen oder einigen der komplexeren Funktionen von Sprachen wie C # und Java beschäftigen. Dies ist sowohl ein Segen als auch ein Fluch.

Die "Härte" von JavaScript ist bei Betrachtung des folgenden Bildes klar ersichtlich:

Das winzige winzige Buch auf der linken Seite ist Douglas Crockfords MUST READ-Buch, JavaScript: Die guten Teile. Nebenan ist auf der rechten Seite, JavaScript Der definitive Leitfaden, von David Flanagan.

Während beide Bücher ausgezeichnete Lesungen sind, zeigt The Good Parts, dass JavaScript zwar eine Menge Sachen enthält, die guten Teile jedoch in einer wesentlich kürzeren Lektüre zusammengefasst werden können. Wenn Sie also nach einer guten, schnellen Lektüre suchen, gehen Sie zu The Good Parts - und lesen Sie es einige Male!

Dies führte natürlich zu vielen schlaflosen Nächten für Webentwickler.

Sie können hier einen Artikel über die Geschichte von JavaScript lesen. Der Kernpunkt ist jedoch, dass Brandon Eich 1995 von Netscape beauftragt wurde, eine Sprache zu entwerfen. Er kam auf die locker getippte Sprache, die wir als JavaScript kennen. Im Laufe der Jahre wurde es als ECMAscript "standardisiert", aber während der gesamten Browser-Kriege implementierten die verschiedenen Browser diese Funktionen unterschiedlich. Dies führt natürlich zu vielen schlaflosen Nächten für Webentwickler. In Verbindung mit der Tatsache, dass JavaScript für die Bearbeitung von Bildern und die Durchführung schneller Validierungsschritte am besten geeignet war, führte dies dazu, dass JavaScript fälschlicherweise als eine schreckliche Sprache angesehen wurde.

Es ist Zeit, das zu beheben! Ja, ja, es gibt viele schlechte Dinge über JavaScript. Wenn es richtig verwendet wird, kann es eine fantastische Sprache sein - und seine dynamische Natur wird mit Ihnen wachsen!


Besser machen

Namensräume

Eine der Nachteile der Implementierung von JavaScript besteht darin, dass es auf a basiert global Objekt. Bei Browsern wird dies die Fenster Objekt. Immer wenn dieser Code auf einer Seite vorhanden ist ...

 function doStuff () alert ('Ich mache Sachen');  function doMoreStuff () var images = document.images.length; console.log ("Es gibt" + Bilder + "auf dieser Seite");  Sachen machen(); doMoreStuff ();

Die Funktionen Sachen machen und das doMoreStuff Funktionen stehen der globalen sofort zur Verfügung Fenster Objekt.

Das heißt, wenn jemand kommt und versucht, eine Funktion zu schreiben, die auch aufgerufen wird, Sachen machen, es wird einen Konflikt geben! Alles Skript Tags nehmen im Grunde den Code in sich auf und führen ihn gegen den Fenster in der Reihenfolge, in der sie im HTML referenziert werden. Dadurch kann die zweite Person umgesetzt werden Sachen machen wird den ersten überschreiben Sachen machen.

Eine übliche Technik zum Beseitigen dieses Problems besteht darin, entweder anonyme Funktionen, die sich selbst ausführen, oder Namespaces zu nutzen. Die objektorientierten Leser, die dies lesen, sind wahrscheinlich bereits mit dem Konzept eines Namensraums vertraut, aber die Grundidee besteht darin, Funktionen in verschiedenen Bereichen zur Wiederverwendbarkeit zu gruppieren.

 var NS = NS || ; // "Wenn NS nicht definiert ist, machen Sie es gleich einem leeren Objekt" NS.Utils = NS.Utils || ; NS.Models = NS.Models || ; NS.Views = NS.Views || ;

Dadurch wird die Verschmutzung des globalen Namensraums verhindert und die Lesbarkeit Ihrer Anwendung wird verbessert. Jetzt definieren Sie einfach Funktionen in ihrem jeweiligen Namensraum. Ein allgemein definierter Namespace ist App, die den Rest der Anwendung verwaltet.

Entwurfsmuster und -praktiken

In jeder Sprache gibt es eine Reihe von Entwurfsmustern. Addy Osmani sagt…

Entwurfsmuster sind wiederverwendbare Lösungen für häufig auftretende Probleme beim Softwaredesign.

Es gibt viele, und wenn sie richtig verwendet werden, können sie die Wartungseigenschaften Ihrer Anwendung erheblich beeinträchtigen. Addy hat ein großartiges JavaScript-Designmuster-Buch geschrieben, das als Essential Design Patterns bezeichnet wird. Lesen Sie es unbedingt!

Ein anderes häufig verwendetes Muster ist das Das Aufdecken des Modulmusters.

 NS.App = (function () // Initialisieren Sie die Anwendung. Var init = function () NS.Utils.log ('Anwendung wurde initialisiert ...');; // Gibt die öffentlichen Methoden für die App zurück. Init: drin ; ()); NS.App.init ();

Oben ein App Funktion ist innerhalb der definiert NS Objekt. Im Inneren eine Funktionsvariable für drin wird definiert und als zurückgegeben anonymes Objektliteral. Beachten Sie, dass am Ende die zusätzlichen Klammern vorhanden sind: ());. Dies zwingt die NS.App Funktion zum automatischen Ausführen und Zurückkehren. Jetzt können Sie anrufen NS.App.init () um Ihre App zu initialisieren.

Die anonyme Funktion oben ist eine bewährte Methode in JavaScript und wird als a bezeichnet Anonyme Funktion zur Selbstausführung. Da Funktionen in JavaScript einen eigenen Gültigkeitsbereich haben - d. H. Variablen, die innerhalb von Funktionen definiert sind, sind außerhalb von ihnen nicht verfügbar -, werden anonyme Funktionen in mehrfacher Hinsicht nützlich.

 // Wickeln Sie Ihren Code in eine SEAF-Funktion (Funktion (global) // Nun sind alle Variablen, die Sie hier deklarieren, außerhalb nicht verfügbar. Var somethingPrivate = 'Sie können nicht zu mir gelangen!'; Global.somethingPublic = ', Sie können jedoch dazu ich! '; (Fenster)); console.log (window.somethingPublic); // Das funktioniert… console.log (somethingPrivate); // Error

Da in diesem Beispiel diese Funktion automatisch ausgeführt wird, können Sie die Funktion übergeben Fenster in den ausführenden Teil (Fenster));, und es wird als zur Verfügung gestellt global innerhalb der anonymen Funktion. Diese Praxis begrenzt die globalen Variablen auf der Fenster Objekt und hilft dabei, Namenskollisionen zu verhindern.

Jetzt können Sie SEAFs in anderen Bereichen Ihrer Anwendung einsetzen, um den Code modularer zu gestalten. Dies ermöglicht, dass Ihr Code wiederverwendet werden kann, und fördert eine gute Trennung der Bedenken.

Hier ist ein Beispiel für eine mögliche Verwendung dieser Ideen.

 (function ($) var welcomeMessage = 'Willkommen bei dieser Anwendung!'. NS.Views.WelcomeScreen = function () this.welcome = $ ('# welcome');; NS.Views.WelcomeScreen.prototype = showWelcome : function () this.welcome.html (welcomeMessage) .show ();; (jQuery)); $ (function () NS.App.init ();); // Ändern Sie die App.init-Datei oben var init = function () NS.Utils.log ('Anwendung wurde initialisiert ...'); this.welcome = new NS.Views.WelcomeScreen (); this.willkommen.showWelcome (); ;

Also, oben gibt es ein paar verschiedene Dinge. zuerst, jQuery wird als Argument an die anonyme Funktion übergeben. Dies stellt sicher, dass die $ ist eigentlich jQuery in der anonymen Funktion.

Als nächstes gibt es eine private Variable namens Willkommensnachricht, und eine Funktion ist zugewiesen NS.Views.WelcomeScreen. Innerhalb dieser Funktion, dies.willkommen ist einem jQuery-DOM-Selektor zugewiesen. Dies speichert den Selektor im welcomeScreen, so dass jQuery das DOM nicht mehr als einmal abfragen muss.

DOM-Abfragen können speicherintensiv sein. Stellen Sie daher sicher, dass Sie sie so viel wie möglich im Cache speichern.

Als nächstes wickeln wir die App ein drin innerhalb $ (Funktion () );, das ist das gleiche wie zu tun $ (Dokument) .ready ().

Zum Schluss fügen wir dem App-Initialisierer etwas Code hinzu. Dadurch bleibt Ihr Code schön und getrennt, und Sie können an einem späteren Tag erheblich einfacher darauf zurückgreifen und Änderungen vornehmen. Mehr Wartbarkeit!

Beobachtermuster

Ein weiteres hervorragendes Muster ist das Observer Pattern - manchmal auch als "Pubsub" bezeichnet. Pubsub ermöglicht es uns im Wesentlichen, DOM-Ereignisse wie klicken und Mouseover. Einerseits sind wir Hören zu diesen Ereignissen und zum anderen veröffentlicht diese Ereignisse etwas, beispielsweise wenn der Browser veröffentlicht (oder ankündigt), dass jemand auf ein bestimmtes Element geklickt hat. Es gibt viele Bibliotheken für pubsub, da es nur ein wenig Code ist. Führen Sie eine schnelle Google-Suche durch, und Tausende von Auswahlmöglichkeiten werden verfügbar. Eine gute Wahl ist die Implementierung von AmplifyJS.

 // Ein Datenmodell zum Abrufen von Nachrichten. NS.Models.News = (function () var newsUrl = '/ news /' // Abrufen der News var getNews = function () $ .ajax (url: newsUrl-Typ: 'get', success: newsRetrieved) ;; var newsRetrieved = function (news) // Abruf der News veröffentlichen amplify.publish ('news-retrieved', news);; return getNews: getNews; ());

Dieser Code definiert ein Modell, um Nachrichten von einem Dienst abzurufen. Sobald die Nachrichten mit AJAX abgerufen wurden, wird die newsRetrieved Die Methode feuert, leitet die abgerufenen Nachrichten an Amplify weiter und wird zum Thema mit den abgerufenen Nachrichten veröffentlicht.

 (function () // Erstellen Sie eine Newsansicht. NS.Views.News = function () this.news = $ ('# news'); // Abonnieren Sie das News Retrieval-Ereignis. amplify.subscribe ('newss-) ', $ .proxy (this.showNews));; // Zeigt die Nachricht an, wenn sie eintrifft. NS.Views.News.prototype.showNews = function (news) var self = this; $ .each (news, function (Artikel) self.append (Artikel););; ());

Dieser Code oben ist eine Ansicht zum Anzeigen der abgerufenen Nachrichten. In dem Nachrichten Konstruktor, Amplify abonniert das abrufbare Thema. Wenn dieses Thema veröffentlicht ist, wird die showNews Funktion wird entsprechend ausgelöst. Dann werden die Nachrichten an das DOM angehängt.

 // Ändern Sie das oben angegebene App.init. Var init = function () NS.Utils.log ('Application initialized ...'); this.welcome = new NS.Views.WelcomeScreen (); this.willkommen.showWelcome (); this.news = new NS.Views.News (); // Los, hol die Nachrichten! NS.Models.News.getNews (); ;

Ändern Sie erneut die drin Funktion von der App aus, um den Nachrichtenabruf hinzuzufügen ... und fertig! Nun gibt es separate Teile der Anwendung, von denen jeder für eine einzelne Aktion verantwortlich ist. Dies ist bekannt als Grundsatz der Einzelverantwortung.


Dokumentation und Dateien / Minification

Einer der Schlüssel zu wartungsfähigem Code jeglicher Art - nicht nur JS - ist Dokumentation und Kommentierung. Kommentare können dazu führen, dass neue Entwickler, die in ein Projekt einsteigen, unbesiegbar sind und wissen müssen, was im Code passiert. "Warum habe ich diese eine Zeile noch einmal geschrieben?" Ein hervorragendes Werkzeug zur Erstellung von Dokumentation heißt Docco. Dies ist das gleiche Tool, das die Dokumentation für die Website Backbone.js generiert. Im Grunde nimmt es Ihre Kommentare und stellt sie neben Ihren Code.

Es gibt auch Tools wie JSDoc, die eine API-Stildokumentation generieren, die jede Klasse in Ihrem Code beschreibt.

Eine andere Sache, die sich beim Starten eines neuen Projekts als schwierig erweisen kann, besteht darin, herauszufinden, wie Sie Ihren Code am besten organisieren. Eine Möglichkeit besteht darin, die Funktionen in separate Ordner zu unterteilen. Zum Beispiel:

  • /app.js
  • /libs/jquery.js
  • /libs/jquery-ui.js
  • /users/user.js
  • /views/home.js

Diese Struktur hilft, die Funktionsteile voneinander zu trennen. Es gibt natürlich verschiedene Möglichkeiten, Code zu organisieren, aber alles, worauf es ankommt, ist, sich für eine Struktur zu entscheiden… und dann mit ihr zu rollen. Als Nächstes können Sie ein Build- und Minification-Tool verwenden. Es gibt viele Möglichkeiten:

  • Grunzen
  • Google-Schließung
  • JSMin
  • YUI Kompressor

Diese Tools entfernen Leerzeichen, entfernen Kommentare und kombinieren alle angegebenen Dateien zu einer. Dadurch werden die Dateigrößen und HTTP-Anforderungen für die Anwendung reduziert. Besser noch: Dies bedeutet, dass Sie alle Ihre Dateien während der Entwicklung getrennt, aber für die Produktion kombinieren können.


AMD

Die asynchrone Moduldefinition ist eine andere Art, JavaScript-Code zu schreiben.

Asynchronous Module Definition ist eine andere Art, JavaScript-Code zu schreiben. Es unterteilt den gesamten Code in separate Module. AMD erstellt ein Standardmuster zum Schreiben dieser Module, um asynchron in Code zu laden.

Verwenden Skript Tags blockiert die Seite, während sie geladen wird, bis das DOM bereit ist. Wenn Sie so etwas wie AMD verwenden, kann das DOM daher weiter geladen werden, während die Skripts ebenfalls geladen werden. Im Wesentlichen ist jedes Modul in eine eigene Datei unterteilt, und dann gibt es eine Datei, die den Prozess startet. Die beliebteste Implementierung von AMD ist RequireJS.

 // main.js requir (['libs / jquery', 'app.js']), function ($, app) $ (function () app.init (););); // app.js define (['libs / jquery', 'views / home']), function ($, home) home.showWelcome ();); // home.js define (['libs / jquery'], function ($) var home = function () this.home = $ ('# home');; home.prototype.showWelcome = function () this.home.html ('Willkommen!');; neues Zuhause (););

Im obigen Code-Snippet befindet sich a main.js Datei, wo der Prozess beginnt. Das erste Argument zum benötigen Funktion ist ein Array von Abhängigkeiten. Diese Abhängigkeiten sind eine Liste von Dateien, für die erforderlich ist app.js. Wenn das Laden abgeschlossen ist, wird das, was das Modul zurückgibt, als Argument an den Funktionsrückruf auf der rechten Seite übergeben.

Dann ist da app.js, Das erfordert jQuery sowie eine Ansicht. Als nächstes die Ansicht, home.js, erfordert nur jQuery. Es hat ein Zuhause Funktion innerhalb und gibt eine Instanz von sich selbst zurück. In Ihrer Anwendung werden diese Module alle in separaten Dateien gespeichert, sodass Ihre Anwendung sehr wartbar ist.


Fazit

Die Wartung Ihrer Anwendungen ist für die Entwicklung äußerst wichtig. Es reduziert Fehler und vereinfacht das Beheben von Fehlern, die Sie finden.

"Freunde lassen Freunde keinen Spaghetti-Code schreiben!"