Innere Funktionen
Closures schotten Daten ab, denn die innere Funktion ist nur in der umgebenden Funktion verfügbar und bindet Variablen an eine Umgebung (Scope). ECMAScript erlaubt innere Funktionen: Die Definition und die Anweisungen einer inneren Funktion sitzen innerhalb einer anderen Funktion. Die innere Funktion kann auf alle lokalen Variablen, auf die Argumente und andere innere Funktionen der umfassenden Funktion zugreifen.
Javascript-Funktionen sind Objekte höherer Ordnung und können wie jeder andere Datentyp einer Variablen als Wert zugewiesen werden. Wenn eine Funktion z.B. einen Integer-Wert zurückgibt, wird sie wie ein Integer behandelt.
- Funktionen können in Variablen gespeichert werden und können der Rückgabewert einer Funktion sein.
- Funktionen können sie als Parameter an andere Funktionen übergeben und wie jedes andere Objekt (String, Array, Number, …) verwendet werden.
- In Javascript dürfen Funktionen in Funktionen erzeugt werden – ein Feature, dass es nur in wenigen Programmiersprachen gibt.
- Funktionen, die einen Rückgabewert liefern, können als Argumente in Funktionen eingesetzt werden.
Da Funktionen Objekt sind, werden sie als Referenz übergeben und nicht als Wert (siehe auch call by reference und call by value).
Javascript Closures
Seit Javascript 1.5 kann eine innere Funktion innerhalb eines Anweisungsblocks erzeugt werden. Allerdings sind dann nur Funktionsaufrufe innerhalb des Blocks zugelassen.
Beim Aufruf der äußeren Funktion (also bei der Ausführung) erzeugt der Javascript-Interpreter den Code der inneren Funktion. Dabei entsteht ein Closure der inneren Funktion – das ist der Code der Funktion und eine Referenz auf alle Variablen, die von der inneren Funktion benötigt werden (die innere Funktion hat Zugriff auf die Variablen und Parameter der äußeren Funktion).
Ein Closure ist eine Funktion, die Zugriff auf den Scope ihrer umfassenden Funktion hat, selbst nachdem die umschließende Funktion abgeschlossen wurde.
Closures kombinieren den Programmcode mit der lexikalischen Umgebung – d.h., ein Closure merkt sich die Umgebung oder den Geburtsort, in der es erzeugt wurde.
Mankos von Closures
Closures haben ihre Schattenseiten: Sie sind anfällig für Memory Leaks und sind langsamer als eine innere Funktion ohne Closure und viel langsamer als eine statische Funktion.
function initAlert () { const msg = "Was noch zu sagen wäre"; window.setTimeout (function () {alert (msg); }, 100); }
ist langsamer als
function initAlert () { window.setTimeout ( function () { const msg = "Was noch zu sagen wäre"; alert (msg); }, 100); }
und auch diese Variante ist langsamer als
function alertMsg() { const msg = "Was noch zu sagen wäre"; alert (msg); } function initAlert () { window.setTimeout (alertMsg, 100); }
Closures erzeugen einen zusätzlichen Level in der Tiefe der Scopes, den der Browser auflösen, bevorraten und prüfen muss.
Beispiel: Funkionen in Funktion
Ein altes Beispiel – heute würden wir CSS dafür benutzten, aber es verdeutlicht schön, wie Closures funktionieren.
function fade (id) { const dom = document.getElementById(id); let level = 1; function step() { // innere Funktion let h = level.toString(16); dom.style.backgroundColor = "#FFFF" + h + h; if (level < 15) { level += 1; setTimeout (step, 100); } } setTimeout(step, 100); // Funktion als Parameter einer Funktion } document.getElementById("foo").onmouseover = function () { let id = this.getAttribute("id"); fade (id); }
Der gesamte Code-Block wird beim Aufruf des Scripts übersprungen. Erst wenn der Benutzer mit der Maus über das Element foo hovert, führt der Javascript-Interpreter die Funktion fade aus.
Die Funktion fade hat zwei Variablen: dom und level. Die innere Funktion step hat Zugriff auf diese Variablen, und bei jedem Lauf durch step ändert die innere Funktion den Wert von level, obwohl die Funktion fade längst zurückgekehrt ist.
Selbst wenn die Funktion fade simultan für drei oder vier oder mehr Elemente aufgerufen wird, kommen sich die Aufrufe nicht ins Gehege. Jede Ausprägung von fade hat ihren eigenen Satz von Variablen dom und level.
Closure-Beispiel: Funktion mit innerem Counter
Hier haben wir eine Funktion greeter mit einem Argument salutation, die eine interne Variable counter definiert.
Der Rückgabewert der Funktion ist eine andere Funktion, die counter bei jedem Aufruf um 1 hoch zählt.
function greeter (salutation) { let counter = 0; let prefix = ". " + salutation + " "; return function (name) { counter ++; return counter + prefix + name + "!"; } } let greet = greeter ("Hello");
Der Zustand der Variablen counter bleibt nach der Rückkehr aus der Funktion erhalten – so kann counter inkrementiert werden, obwohl counter keine globale Variable ist.
Callback Funktionen
Javascript Callback-Funktionen folgen einem einfacheren Konzept. Ein Callback entsteht, wenn eine Funktion eine Funktion als Parameter zulässt. An irgendeinem Punkt der Ausführung wird die Funktion die Callback-Funktion ausführen – das ist das Callback am Callback. Callback-Funktionen werden oft bei der asynchronen Verarbeitung von Events eingesetzt – die Funktion kann fertig ausgeführt sein und die Steuerung bereits zurückgegeben haben, wenn die Callback-Funktion aufgerufen wird.
Callbacks kennen wir seit ewig und drei Jahren aus setTimeout() und setInterval() (und auch requestAnimationFrame setzt auf Callbacks), und anderen Funktionen von window.
function fn() { alert('Hier ist ein Callback') } window.setTimeout(fn, 5000); … elem.addEventListener ('click', myfunction );
Stark vereinfacht ist ein Callback ausführbarer Code, der als Parameter übergeben wird.