JavaScript: Funktionen

Einführung

Mit Funktionen können Sie flexible Teilprogramme notieren, die sie wiederholt aufrufen können. Funktionen sind ein einfaches Mittel, ein JavaScript-Programm zu strukturieren.

In JavaScript haben Funktionen einen höheren Stellenwert als in anderen Programmiersprachen und haben einige Besonderheiten. JavaScript besitzt Aspekte einer funktionalen Programmiersprache. In JavaScript sind Funktionen nicht feste, einmal definierte Teilprogramme, sondern selbst Werte, mit denen Sie im Programm arbeiten können. Sie können neue Funktionen zur Laufzeit erzeugen. Nicht mehr benötigte Funktionen werden wieder gelöscht.

Sie können eigene Funktionen auf verschiedene Arten definieren. Sie können Funktionen als lokale Variable in einer anderen Funktion erzeugen. Sie können Funktionen auch an Objekte hängen. Das bedeutet, sie als Eigenschaft eines Objekts zu speichern. Der Fachbegriff dafür in der objektorientierten Programmierung ist Methode. Und schließlich können Sie auch anonyme (namenlose) Funktionen anlegen, um sie z.B. als Parameter an eine andere Funktion zu geben.

Funktionsdeklarationen

Die einfachste und wichtigste Art, eine Funktion zu definieren, ist die sogenannte Funktions-Deklaration (auf englisch function declaration). Deren Syntax ist folgendermaßen aufgebaut:

function Funktionsname(Parameterliste) {
  Anweisungen
}

Der Funktionsname muss den üblichen Anforderungen an JavaScript-Bezeichner genügen: Sie dürfen Buchstaben, Dezimalziffern (0-9), das Dollarzeichen ($) sowie den Unterstrich (_) verwenden. Ziffern dürfen nicht an erster Stelle stehen.

Die Parameterliste zwischen den beiden runden Klammern ist eine durch Kommas getrennte Liste von Namen. Für diese gelten die besagten Namenskonventionen. Innerhalb der Funktion können Sie auf die übergebenen Parameter zugreifen, indem Sie die hier vergebenen Namen verwenden.

Zwischen den beiden geschweiften Klammern wird der Funktionskörper notiert: Darin werden die Anweisungen untergebracht, die beim Aufruf der Funktion ausgeführt werden sollen.

Folgendes Beispiel soll das Schema verdeutlichen:

function statusMeldung(meldungsTyp, meldungsText) {
  var ausgabeElement = document.getElementById('meldungsausgabe');
  ausgabeElement.className = meldungsTyp;
  ausgabeElement.innerHTML = meldungsText;
}
statusMeldung(
  'fehler',
  'Beim Absenden Ihrer Nachricht ist ein Fehler aufgetreten.'
);

Das Beispiel definiert eine Funktion mit dem Namen statusMeldung. Die Funktion erwartet zwei Parameter mit den Namen meldungsTyp und meldungsText. Sie sucht ein Element im Dokument mit der ID meldungsausgabe. Dann setzt sie dessen Klasse auf den Wert des Parameters meldungsTyp und füllt das Element mit dem Wert des Parameters meldungsText.

Eine Funktion wird nach dem Schema Funktionsname(Parameterliste) aufgerufen. Die Parameterliste beim Aufruf ist eine durch Komma getrennte Liste an Ausdrücken (Namen oder Werten). Der Funktion im Beispiel werden zwei Parameter übergeben, die als String-Literale notiert werden.

Dies führt dazu, dass meldungsTyp in der Funktion den Wert 'fehler' bekommt und meldungsText den Wert 'Beim Absenden Ihrer Nachricht ist ein Fehler aufgetreten.'.

Rückgabewert (Ergebnis)

Eine Funktion kann nicht nur Werte entgegen nehmen, sondern auch einen Wert zurückgeben. Dieser Wert heißt Rückgabewert oder auch Ergebnis. Der Wert kann an der Stelle benutzt werden, an der der Funktionsaufruf steht. Der Funktionsaufruf ergibt den Wert.

Denken Sie an eine einfache mathematische Funktion: f(x) = x². Für x = 5 ergibt die Funktion 25, für x = 6 ergibt die Funktion 36 usw.

In JavaScript könnte die Funktion so aussehen:

function quadrat(x) {
  return x * x;
}
window.alert( quadrat(5) );

Mit der return-Anweisung können Sie die Funktion beenden und einen Wert zurückgeben. Nach dem Schlüsselwort return folgt ein beliebiger Ausdruck. Die Anweisung wird mit einem Semikolon abgeschlossen.

Im Beispiel wird die Funktion quadrat mit dem Parameter 5 aufgerufen. Der Ausdruck x * x ergibt 25, also wird der Wert 25 zurückgegeben.

Das Ergebnis 25 wird an der Stelle eingefügt, an der der Funktionsaufruf stand. Aus window.alert( quadrat(5) ) wird also window.alert( 25 ).

Bei der Funktionsausführung kann return nur einmal ausgeführt werden, denn es beendet die Ausführung sofort und sorgt dafür, dass die Ausführung dort weitergeht, wo die Funktion aufgerufen wurde.

Das heißt aber nicht, dass Sie in einer Funktion nicht mehrere return-Anweisungen verwenden können. return kann mehrfach vorkommen, muss aber in unterschiedlichen logischen Zweigen stehen. Zum Beispiel:

function begrüßung(person) {
  if (person.nickname) {
    return 'Willkommen ' + person.nickname;
  } else {
    return 'Hallo ' + person.vorname;
  }
}

var person1 = {
  vorname: 'Erika'
};
var person2 = {
  vorname: 'Max',
  nickname: 'zimtschnecke'
};

window.alert( begrüßung(person1) );
window.alert( begrüßung(person2) );

Das Beispiel definiert eine Funktion begrüßung, die ein person-Objekt als Parameter erwartet. Darin befindet sich eine bedingte Anweisung. Sie prüft, ob das übergebene Objekt eine Eigenschaft nickname besitzt. Falls ja, gibt sie eine Begrüßung zurück, die nickname verwendet. Falls nein, gibt sie eine Begrüßung zurück, die die Eigenschaft vorname verwendet.

Die Funktion wird zweimal aufgerufen. Einmal mit dem Objekt person1, das einen Nickname besitzt. Einmal mit dem Objekt person2, das keinen Nickname besitzt. Die Fallunterscheidung in der Funktion bestimmt nun den Rückgabewert: Im ersten Fall wird 'Hallo Erika!' zurückgegeben, im zweiten Fall 'Willkommen zimtschnecke'.

Die return-Anweisung beendet die Funktionsausführung wie gesagt. Das heißt, wir können die Funktion begrüßung auch folgendermaßen definieren:

function begrüßung(person) {
  if (person.nickname) {
    return 'Willkommen ' + person.nickname;
  }
  return 'Hallo ' + person.vorname;
}

Sie können sich den else-Zweig sparen. Wenn die Bedingung der if-Anweisung erfüllt ist, springt der JavaScript-Interpreter sofort aus der Funktion heraus. Dieses Muster wird häufig verwendet, um eine Funktion vorzeitig zu beenden. Ein weiteres Beispiel:

function hypotenuse(a, b) {
  if (a <= 0 || b <= 0) {
    return 0;
  }
  var c = Math.sqrt(a * a + b * b);
  return c;
}

Das Beispiel berechnet die Länge der Hypotenuse in einem rechtwinkligen Dreieck anhand des Satzes des Pythagoras. Die Formel dafür lautet c = a 2 + b 2 .

Wenn die Längen der Katheten a und b kleiner oder gleich 0 sind, dann ist eine Berechnung nicht möglich oder würde einen fehlerhaften Wert ergeben. Gegebenenfalls wird die Funktion vorzeitig beendet und sie gibt 0 zurück. Erst nach dieser Prüfung folgt die eigentliche Berechnung der Hypotenuse c.

Funktionsvariablen

Innerhalb von Funktionen können Sie Variablen deklarieren, deren Geltungsbereich auf die Funktion beschränkt ist. Die obige Beispielfunktion hypotenuse hat eine Variable definiert, nämlich c:

function hypotenuse(a, b) {
  if (a <= 0 || b <= 0) {
    return 0;
  }
  var c = Math.sqrt(a * a + b * b);
  return c;
}

Die Variable ist nur innerhalb der Funktion verfügbar, aber deren Wert soll nach außen gegeben werden. Das ist mit der besprochenen return-Anweisung möglich.

Eine genaue Erklärung von Funktionsvariablen finden Sie im Kapitel über Variablen.

Funktionsausdrücke

Die vorgestellte Funktionsdeklaration vereint zwei Aufgaben: Sie erzeugt eine Funktion und speichert diese gleichzeitig unter einem Namen im aktuellen Geltungsbereich.

Folgende Funktionsdeklaration speichert unter dem Namen quadrat eine Funktion.

function quadrat(x) {
  return x * x;
}

Es ist jedoch nicht immer gewünscht, einer Funktion einen Namen zu geben und diesen im aktuellen Geltungsbereich verfügbar zu machen. Manchmal möchte man eine Funktion erzeugen, ohne sie unter einem Namen verfügbar zu machen.

Es gibt daher noch eine weitere Schreibweise, um Funktionen zu erzeugen: Den Funktions-Ausdruck (englisch function expression).

Syntaktisch gesehen ist eine Funktionsdeklaration eine Anweisung. Ein Funktionsausdruck hingegen ist ein Ausdruck, wie der Name schon sagt. Damit haben beide unterschiedliche Anwendungsmöglichkeiten. Sie können Funktionsausdrücke an mehr Stellen notieren als eine Funktionsdeklaration.

Das Schema eines Funktionsausdruckes sieht folgendermaßen aus:

function (Parameterliste) { Anweisungen }

Ein Funktionsausdruck erzeugt lediglich eine Funktion, speichert sie aber nicht automatisch unter einem Namen. Man spricht daher auch von anonymen (namenlosen) Funktionen.

Das Ergebnis des Ausdruckes ist ein Funktionsobjekt. Sie können es wie jeden anderen Wert in JavaScript beliebig weiterverwenden. Beispielsweise können Sie das erzeugte Funktionsobjekt in einer Variable speichern:

var Funktionsname = function (Parameterliste) { Anweisungen };

Diese Variablenzuweisung mit Funktionsausdruck hat den gleichen Effekt wie die klassische Funktionsdeklaration function Funktionsname(…) {…}. Bis auf einige Sonderfälle sind die beiden Schreibweisen austauschbar.

Was sind nun die Vorteile eines Funktionsausdruckes?

Eine Funktion, die Sie mit einem Funktionsausdruck erzeugen, können Sie direkt an ein Objekt als Methode hängen. Auf diese Weise können Sie Ordnung in Ihre Scripte bringen und zusammenhörige Werte und Funktionen unter einem Objekt gruppieren. Ein Beispiel:

var bildergalerie = {
  abspielen: function () {
    /* … */
  }
};
bildergalerie.abspielen();

Im obigen Beispiel wird eine Objekt mittels Object-Literal erzeugt und in der Varialben bildergalerie gespeichert. Dem Objekt wird eine Methode leeren Objekt eine Methode namens abspielen hinzugefügt.

Die entsprechende Funktion wird als Funktionsausdrucks notiert. Das entstehende Funktionsobjekt wird in der Eigenschaft abspielen gespeichert. Schließlich wird die Funktion als Methode aufgerufen.

Das obige Beispiel ließe sich übrigens auch mit einer Funktionsdeklaration lösen:

function abspielen() {
  /* … */
}
var bildergalerie = {
  abspielen: abspielen
};
bildergalerie.abspielen();

Hier wird zunächst eine Funktion abspielen erzeugt. Dann wird sie in der gleichnamigen Eigenschaft des Objekts bildergalerie gespeichert.

Warum ist es sinnvoller, hier einen Funktionsausdruck zu verwenden?

Zunächst einmal ist die Schreibweise mit Funktionsausdruck kompakter. Wichtiger ist aber, dass nur ein Name, nämlich bildergalerie belegt wird. Die Schreibweise mit Funktionsdeklaration belegt einen weiteren Namen, abspielen. Dies ist unnötig und kann zu Konflikten führen, wenn andere Scripte denselben Namen verwenden.

Funktionsausdrücken werden häufig verwenden, wenn eine Funktion an eine andere Funktion übergeben wird. Das passiert beispielsweise bei der fortgeschrittenen Ereignis-Verarbeitung.

Die Variante mit Funktionsdeklaration sähe so aus:

function init() {
  window.alert('Dokument ist fertig geladen!');
}
window.addEventListener('load', init);

Im Beispiel wird eine Funktion namens init angelegt und als Event-Handler für das load-Ereignis beim window-Objekt registriert. Die Funktion wird der Methode addEventListener die Funktion init als Parameter übergeben.

Diese Schreibweise ist sinnvoll, wenn Sie die Funktion init später noch einmal benötigen. Üblicherweise ist das nicht der Fall: Man braucht die Funktion nur einmal, um sie als Handler zu registrieren. Es ist nicht nötig, sie unter einem Namen zu speichern.

Ein Funktionsausdruck kann den Code vereinfachen. Notieren Sie die Handler-Funktion mit einem Ausdruck und übergeben Sie die erzeugte Funktion direkt an addEventListener:

window.addEventListener('load', function() {
  window.alert('Dokument ist fertig geladen!');
});

Es gibt noch weitere Fälle, in denen das Benennen und Zwischenspeichern einer Funktion unnötig ist. Damit gibt es es weitere Anwendungsmöglichkeiten für Funktionsausdrücke. Im Abschnitt über Closures werden wir darauf zurückkommen.

Variable Parameterzahl: Der arguments-Array

Verschachtelte Funktionen (Closures)

Siehe Einführung in Closures

Funktionale Programmierung: Funktionen als Werte

Funktionen sind ganz normale Objekte mit Eigenschaften und Methoden

Event-Handler und Callback-Funktionen

Funktionale Programmierung

Kontext einer Funktion: Das Schlüsselwort this

Siehe Bedeutung von this

Aufrufweise bestimmt den this-Kontext

objekt.methode() vs. var m = objekt.methode(); m();

Function Binding, Function.prototype.bind

Kontext mit call und apply beeinflussen