CSS, HTML und Javascript mit {stil}

Innere Funktionen / Closures

Javascript Closures sind Funktionen in Funktionen

Closures sind Funktionen, die in Funktionen erzeugt werden. Die innere Funktion ist nur in der umgebenden Funktion verfügbar – ein ungewohntes Konzept, das nur wenige Programmiersprachen kennen.

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).

Beispiel: Scope einer Funktion in einer Funktion
Der »Scope« – Gültigkeitsbereich von Variablen

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, in der es erzeugt wurde.

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 () {
   var msg = "Was noch zu sagen wäre";
   window.setTimeout (function () {alert (msg); }, 100);
}

ist langsamer als

function initAlert () {
    window.setTimeout ( function () {
        var msg = "Was noch zu sagen wäre";
        alert (msg);
    }, 100);
}

und auch diese Variante ist langsamer als

function alertMsg() {
   var 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) {
   var dom = document.getElementById(id);
   var level = 1;
   function step() {       // innere Funktion
      var 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 () {
	var 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) {
    var counter = 0;
    var prefix = ". " + salutation + " ";
    return function (name) {
        counter ++;
        return counter + prefix + name + "!";
    }
}
var 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.

Quelle: Introduction to Javascript and Browser DOM

Javascript Callback

So, das ist schon ausgesprochen esoterisch.

Callback-Funktionen sind ein einfacheres 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 mehr 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.

JAVASCRIPT CLOSURE var x = 10; function outerfun () { var y = 15; function innerfun () { z = 25; later.innerHTML += 'z = ' + (x+y+z); } setTimeout (innerfun,7000); } outerfun(); later.innerHTML = 'Erst Pause, dann rechnen:'; z y x