Wie strukturieren Sie JavaScript? Die Modul-Pattern Edition

Avatar of Chris Coyier
Chris Coyier am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

JavaScript ist insofern interessant, als dass es keine bestimmte Struktur vorgibt. „Bring Your Own Organization“, wenn Sie so wollen. Da ich immer mehr JavaScript in Web-App-Websites schreibe, wird dies immer interessanter. Wie Sie Ihr JavaScript strukturieren, ist sehr wichtig, denn

  1. Richtig gemacht, macht es den Code für andere und für Sie selbst einfacher zu verstehen, wenn Sie Ihren eigenen Code wieder aufrufen.
  2. Eine festgelegte Struktur hilft, zukünftigen Code sauber zu halten und Ihre erklärten Best Practices zu fördern.
  3. Es macht Ihren Code testbar.

Bevor ich weitermache, sei gesagt, dass ich weit davon entfernt bin, ein Meister von JavaScript zu sein. Es gibt viele kluge Leute in der JavaScript-Welt, die das schon länger tun und darüber reden, als ich wusste, was ein div ist.

Dieses Thema beschäftigt mich in letzter Zeit, da ich viel mehr JavaScript für CodePen schreibe, das sehr JavaScript-lastig ist. Wir verwenden seit Beginn das „Module Pattern“ und es hat uns bisher recht gut gedient, denke ich.

Dann habe ich diesen Artikel über die Entstehung der Treehouse-Werbung veröffentlicht und darin auch das Module Pattern für das dortige JavaScript verwendet. Louis Lazaris hat darauf hingewiesen, dass er auch so gerne JavaScript schreibt, und eine kleine Diskussion begonnen. Louis folgte dem mit einem Artikel, der sein bevorzugtes Strukturmuster erklärt.

Das Konzept

Es ist einfacher, solche Konzepte zu verstehen, wenn ein Beispiel zum Aufbau vorhanden ist. Nehmen wir an, wir entwickeln ein Nachrichten-Widget. Es listet einige Nachrichtenartikel auf und hat einen Button, mit dem Sie weitere Nachrichtenartikel laden können.

Das Modul

Bescheidene Anfänge

var NewsWidget = {

};

Nur ein Objekt. Ich mag meine JavaScript-Variablen, die mit einem Kleinbuchstaben beginnen, aber Module haben einen Großbuchstaben am Anfang. Nur eine Konvention, die die Lesbarkeit des Codes erleichtert.

Einstellungen

Das Nachrichten-Widget wird wahrscheinlich einige wichtige Zahlenwerte damit assoziiert haben (z. B. wie viele Artikel darin enthalten sind). Auch einige wichtige Elemente (DOM-Knoten), auf die regelmäßig zugegriffen werden muss.

Es wird eine Reihe von Unterfunktionen dieses Moduls geben, die kleine, spezifische Dinge tun. Viele davon müssen möglicherweise auf diese Einstellungen und „gecachten“ Elemente zugreifen können. Daher werden wir die Einstellungen für jede von ihnen verfügbar machen.

var s,
NewsWidget = {

  settings: {
    numArticles: 5,
    articleList: $("#article-list")
  }

};

Den Zugriff auf die Unterfunktion werden wir gleich besprechen.

Init-Funktion

Um „loszulegen“, wird nur **eine** Funktion aufgerufen. Dies ist bei allen Modulen konsistent, sodass Sie genau wissen, wo Sie nachschauen müssen, wenn Sie mit dem Lesen des Codes beginnen und herausfinden, was wann passiert.

var s,
NewsWidget = {

  settings: {
    numArticles: 5,
    articleList: $("#article-list"),
    moreButton: $("#more-button")
  },

  init: function() {
    // kick things off
    s = this.settings;
  }

};

Das Erste, was die init-Funktion tut, ist, die Variable s (die wir auf derselben Ebene wie das Modul deklariert haben) zu einem Zeiger auf die Einstellungen zu machen. Aufgrund der Stelle, an der s deklariert wurde, bedeutet dies, dass alle Unterfunktionen des Moduls Zugriff auf die Einstellungen haben. Gut gemacht.

Binden von UI-Aktionen

Alex Vasquez hat für uns bei CodePen eine Konvention etabliert, bei der wir eine Funktion nur zum Binden der UI-Events haben. Sie schreiben nie Code, der beim Binden etwas „tut“, Sie binden nur und rufen dann eine andere, passend benannte Unterfunktion auf.

var s,
NewsWidget = {

  settings: {
    numArticles: 5,
    articleList: $("#article-list"),
    moreButton: $("#more-button")
  },

  init: function() {
    s = this.settings;
    this.bindUIActions();
  },

  bindUIActions: function() {
    s.moreButton.on("click", function() {
      NewsWidget.getMoreArticles(s.numArticles);
    });
  },

  getMoreArticles: function(numToGet) {
    // $.ajax or something
    // using numToGet as param
  }

};

Kombinieren von Dateien

Wahrscheinlich ist das Nachrichten-Widget nicht das einzige JavaScript, das Sie auf einer Seite haben werden. Es wird viele Module geben, die Sie laden müssen. Vielleicht befindet sich das Nachrichten-Widget auf jeder Seite Ihrer Website und gehört daher in eine global.js-Datei. Diese global.js-Datei wird letztendlich eine Menge von Modulen sein, die zusammengeführt werden, und dann eine große Auftaktparty.

Es gibt alle möglichen Arten, diese Verkettungsangelegenheiten zu handhaben. Es könnte ein Entwickler-Tool wie CodeKit und seine Fähigkeit, Anhänge/Vorbereitungen durchzuführen, sein. Es könnte ein schickes Grunt.js-Build-Skript-Ding sein.

Bei CodePen verwenden wir Ruby on Rails und seine Asset-Pipeline. Für uns wäre die global.js-Datei also so etwas wie

//= require common/library.js

//= require module/news-widget.js
//= require module/some-other-widget.js

(function() {

  NewsWidget.init();

  SomeOtherModule.init();

})();

Das ist alles, Leute

Ziemlich befriedigende Art, JavaScript zu schreiben, finde ich. Es erfüllt die drei Punkte, die ich zur Bedeutung von Struktur gemacht habe.

Bezüglich des Testpunkts weiß ich, dass er nicht viel diskutiert wurde, aber ich bin sicher, Sie können sich vorstellen, wie einfach es ist, für kleinere, spezifische Funktionen, die für spezifische Aufgaben zuständig sind, Assertionen zu schreiben. So wird die meiste JavaScript-Tests durchgeführt (z. B. Jasmine). Zum Beispiel in irgendeiner Form von Code oder einer anderen: „Ich behaupte, dass, wenn diese Funktion diesen Wert erhält, etwas anderes passiert und gleich einem anderen Wert ist.“

Das ist nur die Spitze des Eisbergs. Ich habe gerade erst damit angefangen, aber Learning JavaScript Design Patterns (kostenlos online zu lesen!) von Addy Osmani scheint viel tiefer in dieses Kaninchenloch zu gehen.