Dies ist der zweite Teil der Reihe JavaScript Coding Patterns. Der erste Teil handelt vom Einsatz von Objekt-Literalen zur Strukturierung von JavaScript-Code. Nun geht es um Closures und Immediate Functions und darum, wie sich mit deren Hilfe private und öffentliche Eigenschaften von JavaScript-Objekten umsetzen lassen.
Funktionen als Objekte
JavasScript ist eine funktionale Sprache und räumt naturgemäß Funktionen einen wichtigen Stellenwert ein. Funktionen sind in JavaScript First-Class Objects. Sie haben Attribute und Methoden, werden an Funktionen als Argumente übergeben, können Variablen als Werte zugewiesen werden und lassen sich dynamisch, zur Laufzeit, definieren. Und Javascript-Funktionen erzeugen Scope.
Vor allem auf das zuletzt genannte Merkmal von JavaScript-Funktionen gehe ich im Folgenden ein und stelle vor diesem Hintergrund die Verwendung von Closures und Immediate Functions als Coding Patterns dar.
Scope
Nur Funktionen stellen in JavaScript lokale Sichtbarkeitsbereiche von Variablen zur Verfügung. In JavaScript gibt es keinen Curly Braces Scope (resp. Block Scope), sondern nur Function Scope. Mit einer Funktion lässt sich also die Sichtbarkeit von Variablen regeln, lässt sich festlegen, was private und was public ist. Jede Funktion hat Zugriff auf ihren eigenen Kontext und zusätzlich auf den aller ihrer Eltern. Das wird die Scope Chain genannt. Im nachfolgenden Beispiel hat die innerste Funktion core() Zugriff auf ihre eigenen und auf alle entlang der Scope Chain stehenden Variablen:
var globalVar = 1; var outer = function () { var outerVar = 2 var inner = function () { var innerVar = 3; var core = function () { var coreVar = 4; return coreVar + innerVar + outerVar + globalVar; }; return core(); }; return inner(); }; outer(); // 10
Für den Fall, dass in den verschachtelten und verketteten Kontexten Namenskonflikte auftreten, weil lokale Variablen den gleichen Namen wie globale erhalten haben, überlagern die lokalen Definitionen die globalen. Der lokale Scope gewinnt immer!
Closures
Bereits das Beispiel der Scope Chain zeigt eine Closure in Aktion. Eine Closure ist eine Funktion, die eine andere Funktion umrahmt. Mit Hilfe solcher Konstrukte kann in JavaScript Datenkapselung realisiert werden. Das ist im nächsten Code-Listing noch einmal genauer zu sehen:
var application = function () { var name = 'Awesome Application'; var version = 'v0.0.1'; var init = function () { var msg = 'Successfully launched: '; return msg + name + ', ' + version; }; return init; }; var initApp = application(); initApp(); // 'Successfully launched: Awesome Application, v0.0.1'
Der Rückgabewert von application() ist die Funktion init(). JavaScript-Funktionen können eben auch Funktionen zurückliefern und nicht nur einfache Daten, Arrays oder Objekte. Die hier zurückgegebene Funktion hat neben ihrem eigenen Scope auch Zugriff auf den sie umgebenden Kontext und kann darum private Daten von application auslesen. Diesen Einschluss einer Funktion in einer anderen Funktion nennt man allgemein Closure, einen Funktionsabschluss.
Was eine Closure ausmacht, ist die Tatsache, dass hier eine Funktion immer eine Verknüpfung zu dem Kontext behält, in dem sie definiert wurde. Im oben gezeigten Beispiel kann über die globale Variable initApp, die auf den ersten Blick nur eine einfache Funktionsreferenz zu sein scheint, auf die lokalen Variablen von application zugegriffen werden. Und das sogar nachdem application() ihren Rückgabewert geliefert hat, wodurch auch ihr Scope zerstört wurde. Die weiter oben beschriebene Scope Chain ist somit aufgebrochen.
Eine Closure ist so gesehen ein ausführbares Stück Code, das in andere Kontexte hineingereicht werden kann und immer noch Zugriff auf Daten seines Entstehungskontextes hat. Dabei erfolgt der Zugriff per Referenz, nicht als Kopie.
Immediate Functions
Ein weiterer interessanter und verbreiteter Anwendungsfall für Funktionen ist die sogenannte Immediate Function. Damit wird eine Funktion bezeichnet, die bereits mit ihrer Definition ausgeführt wird:
(function () { return 'In an instant!'; })();
In diesem Fall wird eine anonyme Funktion, ein bloßer Funktionsausdruck, keiner Variablen zugewiesen. Das ist der Grund dafür, dass der Ausdruck in Klammern gefasst werden muss. Das entscheidende Detail ist aber das abschließende Paar runder Klammern, der Function Call Operator. Er sorgt dafür, dass die anonyme Funktion unmittelbar aufgerufen und der Code in ihr direkt ausgeführt wird. Der Operator bietet selbstverständlich noch die Möglichkeit, Argumente an die anonyme Funktion zu übergeben:
(function (name) { return 'Hello ' + name; })('Willy'); // 'Hello Willy!'
Eine Immediate Function kann z.B. dazu verwendet werden, Prozesse anzustoßen, die initial, vor allen anderen, laufen müssen. Übrigens, viele JavaScript Bibliotheken nutzen eine Immediate Function um ihren Namensraum zu definieren und zu sichern. So besteht beispielsweise der Quellcode von jQuery aus einer einzigen großen Funktion dieser Art.
Fazit
Closures und Immediate Functions sind in jedem Fall dazu geeignet, auch komplexem Code einen Rahmen zu geben. Mit Closures lassen sich private und öffentliche Eigenschaften und Methoden sehr flexibel realisieren, ohne das in JavaScript Konzepte wie public und private existieren. Und mit Immediate Functions können auch größere Code-Blöcke in einem Scope zusammengefasst und sogar direkt ausgeführt werden.
Im dritten Teil der Reihe JavaScript Coding Patterns wird es um die Kombination von Closures und Immediate Functions gehen, um das sogenannte Module Pattern.
Schreibe einen Kommentar