Warum Code organisieren?

Wir haben immer mehr Scripte auf unseren Webseiten:

  • JavaScript-Frameworks
  • Plug-ins
  • Banner, Adwords, Affiliate-Scripts
  • Social Media Plug-ins
  • Webtracking

Warum Code organisieren?

„Lass nicht überall dein Zeug rumliegen!“ – „Jaaa, Mama.“

    // script1.js
    function doStuff() { console.log("I do stuff"); }


    // script2.js
    function doStuff() { console.log("Ha, I don't!"); }
    doStuff();

Codekapselung

What happens in Vegas, stays in Vegas.

Codekapselung

Immediately Invoked Function Expression (IIFE)

  // IIFE
  (function() {
    // Mein Code …
  })();

Codekapselung

Immediately Invoked Function Expression (IIFE)

  // Function
  (function() { 
    // Mein Code …
  })();
  •   // Expression
      (function() {
        // Mein Code …
      })();
    

  •   // Unmittelbare Ausführung
      (function() {
        // Mein Code …
      })();
    

Modularisierung

Objekt-Literale

Die einfachste Modulform in Javascript

    var myModule = {
      myVar       : "Hello!",
      _privateVar : "Ohai!",

      doStuff     : function() {
        console.log( myModule.myVar + " I do stuff");
      }
    };

    myModule.doStuff();
    // => "I do stuff"

    console.log(myModule._privateVar);
    // => "Ohai!"
    // Wait, what?!

Codekapselung und Sichtbarkeit

Definierte Variablen und Methoden nach außen sichtbar machen:

  (function(export) {
    var privateVar        = "foo";
    var export.publicVar = "bar";

    export.myFunction = function() {
      console.log("Hello global namespace!");
    }
  })(window);
  •   console.log(privateVar);
      // => ReferenceError: privateVar is not defined
    

  •   console.log(publicVar);
      // => "bar"
    

  •   myFunction();
      // => "Hello global namespace!"
    

Codekapselung und Sichtbarkeit

Warum „export“?

  (function(export) {
    export.myFunction = function() {
      console.log("Hello global namespace!");
    }
  })();

Der globale Namespace kann sich beliebig händern:

  • Browser: window
  • Node.js: module.exports
  • Applikation: z.B. myApp

Der gekapselte Code selbst muss nicht mehr angepasst werden.

Module Pattern

  var myApp = (function(export) {
    export.myFunction = function() {
      console.log("Hello global namespace!");
    }

    return export;
  })(myApp || {});

Vorteile:

  • Globaler Namespace nach außen (wie beim Objekt-Literal)
  • Codekapselung und Sichtbarkeit (wie bei der IIFE)
  • Code kann so über mehrere Dateien verteilt werden

Enge und lose Kopplung

Wer bin ich, und wenn ja, wie viele?

Enge Kopplung

modul2.js kann Methoden aus modul1.js aufrufen:

  • solange modul2.js von der Existenz von modul1.js weiß …

      // modul2.js
      myApp.funcFromModule1();
    

  • … und beide in der richtigen Reihenfolge eingebunden sind.

      <!-- index.html -->
      <script src="modul1.js"></script>
      <script src="modul2.js"></script>
    

Enge Kopplung

Was bei enger Kopplung beachtet werden muss:

  • Welches Modul verwendet welche anderen Module?
  • In welcher Reihenfolge müssen die Module geladen werden?
  • Was ist, wenn ich weitere Module hinzufügen möchte …
  • … oder andere weglassen/austauschen?
  • Was ist, wenn Modul 1 von Modul 2 abhängt, und umgekehrt?

Kommunikation über Events

Publish-Subscribe-Prinzip (PubSub)

Kommunikation über Events

Publish-Subscribe-Prinzip (PubSub)

  • Kommunikation erfolgt asynchron über Events
  • Funktioniert analog zu Events im DOM (z.B. onclick)
  • Module müssen nicht voneinander wissen
  • Module interagieren nicht direkt miteinander
  • Einfach erweiterbar

Kommunikation über Events

Publish-Subscribe-Prinzip (PubSub)

  $.subscribe('myevent', function(event, param1, param2) {
    console.log('Mein Event wurde ausgelöst:', param1, param2);
  });
  // Entspricht $(document).on(…)
  $.publish('myevent', ['Erster Wert', 'Zweiter Wert']);
  // Entspricht $(document).trigger(…)

Beispiel

Darstellung auf einer Shop-Seite

  // shoppingcart.js
  $.subscribe('quantity:change', function(event, article, quantity) {
    redrawShoppingCart(article, quantity);
  });

  // article-overview.js
  $.subscribe('quantity:change', function(event, article, quantity) {
    checkForDiscount(article, quantity);
  });

  // order-familypack.js
  $.publish('quantity:change', ['Product 1', 5]);

Modul-Standards in JavaScript

Module mit Abhänigkeiten definieren

Modul-Standards in JavaScript

Module mit Abhänigkeiten definieren

  • CommonJS
    wird u.a. in Node.js verwendet:
    module = require('modulename');

  • Asyncronous Module Definition (AMD)
    für browserbasiertes JavaScript
    bekannteste Implementierung: Require.js (requirejs.org)

  • künftig: native Unterstützung (ECMAScript 6)

AMD-Module mit Require.js

  • Lädt Module per Ajax (Asyncronous Module Definition)
  • Verwaltet die Abhängigkeiten untereinander
  • Kann auch Modul-Assets laden (z.B. Templates, CSS)
  • Kann Module für Produktivbetrieb kompilieren
    (Auflösung von Abhängigkeiten, Zusammenfassen & Minifizieren)

AMD-Module mit Require.js

  •   <!-- index.html -->
      <script src="require.js" data-main="app.js"></script>
    

  •   // app.js
      require(['lib/jquery', 'modul1'], function($, modul1) {
        $(function() {
          modul1.myFunction();
        }
      });
    

AMD-Module mit Require.js

Abhängigkeiten innerhalb von Modulen

  •   // modul1.js
      define(function() {
        return {
          myFunction: function() {
            console.log('Hello Require.js!');
          }
        }
      });
    

  •   define(['modul2', 'include/modul3'], function(module2, module3) {
        // …
      });
    

Deployment?

Grunt. ;-)

<Thank You!>