JavaScript/Funktion

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Eine Funktion bezeichnet im Quellcode ein Stück Javascript, das an einer Stelle gespeichert ist und von anderen Stellen aus beliebig oft aufgerufen werden kann. Funktionen verfügen über Parameter, darunter versteht man eine Liste von Variablennamen, die während der Ausführung wie lokale Variablen zur Verfügung stehen und deren Werte - Argumente genannt - vom Aufrufer bereitgestellt werden. Eine Funktion lässt sich so leicht für unterschiedliche Zwecke aufrufen. Zum Abschluss kann eine Funktion einen einzelnen Wert an den Aufrufer zurückgeben.

Dieses Konzept ist Ihnen aus anderen Sprachen möglicherweise unter den Namen Subroutine oder Prozedur bekannt.

Zur Laufzeit wird eine Funktion als ein Objekt dargestellt. Diesem Objekt kann man neue Eigenschaften geben, man kann es an Variablen zuweisen, an andere Funktionen als Argument übergeben oder als Rückgabewert einer weiteren Funktion erhalten.

Funktionsobjekte (nicht zu verwechseln mit dem Function-Objekt!) können auch Eigenschaft eines anderen Objektes sein, dann spricht man von Methoden. Sie sind nicht Thema dieses Artikels.

In JavaScript gibt es sowohl selbst definierte Funktionen als auch objektunabhängige, vordefinierte Funktionen.

Inhaltsverzeichnis

[Bearbeiten] Objektunabhängige Funktionen

Objektunabhängige Funktionen sind im Gegensatz zu selbst definierten Funktionen in JavaScript bereits vordefiniert. Das heißt, Sie können diese Funktionen jederzeit aufrufen, ohne sie vorher selbst definieren zu müssen, da sie bereits in JavaScript integriert sind.

Üblicherweise sind die hier genannten objektunabhängigen Funktionen an das window-Objekt gekoppelt und deshalb im globalen Namensraum verfügbar. Man kann zu ihrem Aufruf das window-Objekt davor notieren (z.B. window.alert()), oder aber auch weglassen, wenn es im gegenwärtigen Geltungsbereich (Scope) keine andere "lokale" Funktion gleichen Namens gibt (z.B. alert()).

[Bearbeiten] Selbst definierte Funktionen

Sie können Ihren eigenen Javascript-Code mit Hilfe von Funktionen gliedern. Nur der Code, der beim Laden der Seite sofort ausgeführt werden soll, muss außerhalb einer Funktion stehen und sollte dann nicht viel mehr tun, als globale Variablen zu initialisieren und den Rest der Arbeit an eine Funktion zu delegieren.

  • JavaScript 1.0
  • Chrome
  • Firefox
  • IE
  • Opera
  • Safari

[Bearbeiten] Funktion erzeugen

In JavaScript werden Funktionen mit dem Schlüsselwort function definiert. Das generelle Erzeugen und Verwenden von Funktionen ist denkbar einfach:

[Bearbeiten] Funktionsdeklaration als Statement

Eine Funktionsdeklaration als Statement (function declaration statement) beginnt mit dem Schlüsselwort function und dem Funktionsnamen, gefolgt von einem Paar runder Klammern und einem Funktionskörper mit einem Anweisungsblock. In den Klammern können, durch Komma getrennt, Parameter für die Funktion spezifiziert werden. Ein Semikolon hinter den geschweiften Klammern ist nicht erforderlich.

Beispiel: Funktionsdeklaration
function foo ([parameter1[, parameter2[,]]]) { 
    /* tue etwas, optional Rückgabe mit return */ 
}

Parameter dienen als Ablageort für Werte, die beim Aufruf der Funktion mitgegeben werden können. Innerhalb des Funktionskörpers verhalten sie sich wie lokale Variablen der Funktion.

Beispiel: Funktion mit Parametern
function celsiusInFahrenheit(celsius) { 
    return celsius * 1.8 + 32;
}

Das Funktionsdeklarations-Statement tut drei Dinge:

  • Es erzeugt in dem Gültigkeitsbereich (Scope), in dem es steht, eine Variable mit dem Namen der Funktion
  • Es erzeugt ein Funktionsobjekt als Instanz des Function-Konstruktors mit den angegebenen Argumenten und dem gegebenen Körper
  • Es weist das Objekt an die Variable zu.

Es lässt sich daher als alternative Schreibweise für einen Funktionsausdruck, der einer Variablen zugewiesen wird, auffassen (siehe nächstes Kapitel).

Genauso wie Variablendeklarationen unterliegt auch das Funktionsdeklarations-Statement dem automatischen Heben (hoisting) an den Beginn des Scopes, in dem es steht. Das hat den Vorteil, dass man eine Funktion auch von Stellen aus aufrufen kann, die vor dem Funktionsdeklarations-Statement stehen. Es hat aber zur Folge, dass man Funktionen damit nicht in untergeordneten Anweisungsblocks deklarieren kann:

Beispiel: Funktion im Anweisungsblock - NICHT ERLAUBT
if (a > 4) {                // Untergeordneter Anweisungsblock beginnt
   function berechne(q) {   // Nicht erlaubt!
      return q*4;
   }
} else {
   function berechne(q) {   // Nicht erlaubt!
      return q*7;
   }
}

Hier würde von zwei Stellen aus die berechne-Funktion an den Beginn des Codes gehoben, was zu einer Mehrdeutigkeit führt. Die Javascript-Engines verhalten sich hier unterschiedlich, es kann durchaus passieren, dass unabhängig vom Wert von a immer die zweite Funktion gewählt wird.

[Bearbeiten] Funktionsausdruck

Ein Funktionsausdruck (function expression) ist fast genauso aufgebaut. Er erlaubt es, die Erzeugung einer Variablen zum Speichern des Funktionsobjekts vom Erzeugen des Funktionsobjekts zu trennen. Ein Funktionsausdruck darf allerdings nicht am Beginn eines Statements stehen, sonst wird er von JavaScript mit einem Funktionsdeklarations-Statement verwechselt. Man muss ihn einer Variablen zuweisen, oder zumindest in Klammern setzen.

Beispiel: Funktionsausdruck, der einer Variablen zugewiesen wird
var func = function ([parameter1[, parameter2[,]]]) { 
    /* tue etwas, optional Rückgabe mit return */ 
};

Da es sich hier um kein autarkes Statement handelt, sondern um einen Ausdruck, ist das Semikolon am Ende erforderlich.

Beispiel: celsius-Umrechner als Funktionsausdruck
var celsiusInFahrenheit = function(celsius) { 
    return celsius * 1.8 + 32;
}

In einem Funktionsausdruck wird hinter function normalerweise kein Name notiert. Das liegt daran, dass das Funktionsobjekt, das von diesem Ausdruck erzeugt wird, an sich namenlos ist. Eine benannte Funktion wird erst durch Zuweisung an eine Variable daraus.

Trotzdem ist es erlaubt, in einem Funktionsausdruck hinter function einen Namen anzugeben. Dieser Name spielt aber eine andere Rolle als im Funktionsdeklarations-Statement. Er erzeugt keine Variable, sondern er dient als Referenzname für den Fall, dass die Funktion im Ausdruck sich selbst aufrufen möchte (siehe rekursive Funktion).

Da Funktionsausdrücke die speichernde Variable von der Funktion separieren, kann man mit ihnen auch Funktionen in Anweisungsblöcken erzeugen:

Beispiel: Funktionsausdruck im Anweisungsblock - ERLAUBT
var berechne;
if (a > 4) {
   berechne = function (q) { return q*4; }
} else {
   berechne = function (q) { return q*7; }
}
 
// oder ganz kompakt:
berechne = a > 4 ? function (q) { return q*4; } : function (q) { return q*7; }

Der wichtigste Unterschied zwischen der Statement- und Ausdruck-Form einer Funktionsdefinition ist, dass das Statement vom ECMAScript-Interpreter vollständig zum Beginn des Scopes gehoben wird, in dem es steht. Wird ein Funktionsausdruck an eine Variable zugewiesen, wird nur die Variablendefinition gehoben. Das wirkt sich aus, wenn eine Funktion aufgerufen wird, bevor sie definiert wurde:

Beispiel: Statement vs Funktionsausdruck
tuwas(3);       // Funktioniert
machwas(5);     // Error: machwas ist keine Funktion
 
function tuwas(q) {
   return q*3;
}
 
var machwas = function(q) {
   return q*5;
}
 
machwas(17);    // Hier funktioniert es.

[Bearbeiten] Funktionskonstruktor

Eine eher selten verwendete Vorgehensweise definiert Funktionen über den Funktions-Konstruktor:

Beispiel
var foo = new Function('[Argument1[, Argument2[, …]]]', 
                       '"tue etwas"; return "Gib etwas zurück"');

Letzteres ist ähnlich eval, es nimmt einen String und macht Javascript-Code daraus.

Empfehlung: Genauso wie im Fall von eval gilt auch hier: nicht verwenden.

[Bearbeiten] Anonyme Funktionen

Das Funktionsobjekt, das von einem Funktionsausdruck erzeugt wird, muss nicht an eine Variable zugewiesen werden. Man kann es genauso gut direkt einer anderen Funktion als Argument übergeben, als Wert in ein Array legen oder es als Wert einer Funktion zurückgeben lassen. An dieser Stelle zeigt sich die Natur von Javascript als funktionaler Programmiersprache: Funktionen sind ganz normale Objekte und können wie diese beliebig herumgereicht werden.

Anonyme Funktionen werden gerne genutzt, um Callbacks für setTimeout oder Handler für DOM Ereignisse bereitzustellen:

Beispiel
   setTimeout(function() { alert("Zeit vorbei!"); }, 2000);
 
   // Aus dem Blog von Todd Motto
   document.querySelector('.menu').addEventListener('click', function (event) {
      // Klick wurde ausgeführt
      if (!this.classList.contains('active')) {
         this.classList.add('active');
      }
      event.preventDefault();
   });

Als nachteilig kann sich hier erweisen, dass man auf diese Weise viele kleine Codeschnipsel erhält, denen der Debugger bei der Fehlersuche keinen Namen mehr zuordnen kann. Landet man in einer tiefen Aufrufhierarchie an einem Breakpoint und findet im Aufrufstack 50% anonyme Funktionen, ist kaum erkennbar, auf welchem Weg der Code dorthin gelangt ist.

Hinzu kommt, dass eine benannte Funktion - bei guter Namenswahl - sich selbst dokumentieren kann, während ein anonymer Codeschnipsel automatisch erklärungsbedürftig ist. Z.B. bedarf der Click-Handler im letzten Beispiel einem Moment des Nachdenkens, bevor man versteht, was er tut. Darüber hinaus kann man den Event-Listener nicht wieder entfernen, und für andere Elemente verwenden kann man ihn auch nicht. Das Folgende dagegen liest sich gleich klarer, weil der Programmierer, der den addEventListener Aufruf liest, nicht die registrierte Funktion verstehen muss, sondern über ihren Namen sofort erfährt was hier beabsichtigt ist.

Beispiel
   // Aus dem Blog von Todd Motto
   var toggleMenu = function(event) {
      if (!this.classList.contains('active')) {
         this.classList.add('active');
      }
      event.preventDefault();
   };
   document.querySelector('.menu').addEventListener('click', toggleMenu);


[Bearbeiten] Funktionen aufrufen

Alle Funktionen, die selbst definierten sowie die vordefinierten, können aufgerufen werden, um den darin enthaltenen JavaScript-Code auszuführen.

  • JavaScript 1.0
  • Chrome
  • Firefox
  • IE
  • Opera
  • Safari
Beispiel ansehen …
     function init() {   
      document.getElementById("PrimZahlCheck").addEventListener("click", rechne);
    };
 
    function PrimzahlCheck (Zahl) {
      Zahl = parseInt(Zahl, 10);
        if (isNaN(Zahl)) {
          alert('Sie müssen eine Zahl eingeben!');
          return;
        }
      var Grenzzahl = Zahl / 2;
      var Check = 1;
        for (var i = 2; i <= Grenzzahl; i++)
          if (Zahl % i == 0) {
            text = Zahl + ' ist keine Primzahl, weil teilbar durch ' + i +'.';
            Check = 0;
          }
      if (Check == 1) {
        var text = Zahl + ' ist eine Primzahl!';
	  }
      var ausgabe = document.getElementById('ausgabe');
      ausgabe.innerHTML = text;
    }
 
    function rechne (){	  
      var zahl = document.getElementById('Eingabezahl').value;
      PrimzahlCheck (zahl);
	  document.getElementById('Eingabezahl').value = "";
	}
In der Funktion init() wird der vorher funktionslose Button mit der id PrimzahlCheck mit addEventListener anklickbar. Wenn der Anwender auf den Button klickt, wird die JavaScript-Funktion rechne() aufgerufen. Sie ermittelt mit document.GetElementById('Eingabezahl').value den eingegebenen Wert und ruft die Funktion PrimzahlCheck (...) auf. Innerhalb der runden Klammer wird der Parameter zahl übergeben, der die Grundlage für weitere Berechnungen ist.

Die Funktion rufen Sie mit ihrem Funktionsnamen auf. Dahinter folgt die öffnende Klammer. Wenn die Funktion keine Parameter definiert, notieren Sie hinter der öffnenden gleich eine schließende Klammer. Sind für die Funktion Parameter vorgesehen, können Sie für jeden Parameter einen erlaubten Wert übergeben. Im Beispiel erwartet die Funktion einen Parameter. Wenn Sie mehrere Parameter übergeben, trennen Sie diese durch Kommata.

[Bearbeiten] Funktionen mit Parametern

Beispiel
  var summe = function (summand1, summand2) {   // (1)
    var summe = summand1 + summand2;            // (2)
    return summe;                               // (3)
  };
  alert(summe(40, 2));                          // (4)
  1. Erzeugt eine Funktion, die zwei Argumente erwartet und weist sie der Variablen "summe" zu.
  2. Der Funktions-Körper. Hier wird die Logik der Funktion notiert. In diesem Beispiel eine einfache Addition.
  3. Eine Funktion kann einen Wert zurückgeben, hier wird das Ergebnis der Addition zurückgegeben. return ist optional.
  4. Ruft die Funktion alert auf und übergibt als Parameter den Rückgabewert der Funktion summe.

Es gibt oft Verwirrung zwischen den Begriffen Parameter und Argumente. Dabei ist es ganz einfach: Die Werte, die man beim Aufruf an eine Funktion übergibt, sind die Argumente. Die Variablen, in denen die Argumente dann innerhalb der Funktion zur Verfügung stehen, sind die Parameter.

Die Übertragung der Argumente in die Parameter erfolgt gemäß ihrer Position. In JavaScript ist es kein Ausführungsfehler, wenn an eine Funktion mehr oder weniger Argumente übergeben werden, als Parameter in ihrer Signatur definiert sind.

  • Falls zu wenige Argumente übergeben wurden, werden die frei bleibenden Parameter mit undefined initialisiert.
  • Falls zu viele Argumente übergeben wurden, werden nur soviele Argumente in Parameter übertragen, wie definiert sind. Auf überzähligen Argumente kann man mittels arguments zugreifen.
  • Ab ECMAScript 2015 (ES6) gibt es zusätzlich die Möglichkeit, einen Rest-Parameter zu definieren, der die restlichen Parameter als Array aufnimmt. Diesen notiert man mit drei vorangestellten Punkten.

Funktionen, deren Parameteranzahl nicht festgelegt ist und die eine variable Anzahl von Parametern verarbeiten können, werden auch variadische Funktionen genannt.

Beispiel: Variadische Funktion mit Restparameter
function baueNachricht(info, ...teile) {
   return info + ": " + teile.join(', ');
}
 
alert(baueNachricht("Finde die Lösung in", "SelfHTML", "MDN", "Wikipedia"));
Ergibt: Finde die Lösung in: SelfHTML, MDN, Wikipedia

[Bearbeiten] Funktionen mit Rückgabewert

Eine Funktion kann genau einen Wert an die aufrufende Instanz zurückgeben.

  • JavaScript 1.0
  • Chrome
  • Firefox
  • IE
  • Opera
  • Safari
Beispiel ansehen …
    function init() {   
      document.getElementById("BruttoForm").addEventListener("click", rechne);
    };
 
    function BruttoBetrag (Netto, Prozente) {
      var ergebnis = Netto * (1 + (Prozente / 100));
      return ergebnis;
    };
 
    function rechne () {
      var betrag = document.getElementById('NettoEingabe').value,
          prozentsatz  = document.getElementById('ProzentEingabe').value,
          ausgabe = document.getElementById('ausgabe');
 
      var ergebnis  = BruttoBetrag(betrag, prozentsatz);
      ausgabe.innerHTML = ergebnis;
    };
Im JavaScript-Bereich befinden sich drei Funktionen. In der Funktion init wird der vorher funktionslose Button mit addEventListener anklickbar. Wenn der Anwender nun auf den Button klickt, wird die Funktion rechne () aufgerufen, die die eingegebenen Werte ermittelt.

Daraufhin wird die Funktion BruttoBetrag(...) aufgerufen, die jetzt das Ergebnis berechnet.

Da die Funktion BruttoBetrag(...) ihr errechnetes Ergebnis an die aufrufende Instanz zurückgibt, wird in rechne() eine Variable mit dem Namen ergebnis definiert, die diesen Rückgabewert speichert. Das Ergebnis, das in dieser Variablen gespeichert ist, schreibt die Funktion schließlich in den Absatz mit der id ausgabe.

Funktionen können ein oder mehrere return-Statements beinhalten. Diese beenden die Funktion und definieren den Rückgabewert. Falls return innerhalb einer Funktion nicht aufgerufen wird, wird die Funktion nach deren letztem Statement verlassen und undefined zurückgegeben.

Beachten Sie: Zwischen return und dem Wert, der zurückgegeben werden soll, dürfen keine Zeilenumbrüche sein. JavaScript glaubt sonst, man hätte ein Semikolon vergessen, und fügt automatisch eins ein.
Beispiel
var foo = function () {
    return
      true;
  };
foo(); // Gibt undefined zurück, da nach return ein Zeilenumbruch kommt

Es ist nicht möglich, mehr als einen Wert aus einer Funktion zurückzugeben. Da es sich bei einem Wert aber auch um ein Array oder ein Objekt handeln kann, lassen sich auf diese Weise mehrere Werte zu einem verkapseln, wenn umfangreichere Rückgaben benötigt werden.

[Bearbeiten] Variablen Scope

Mit Scope ist die Bereich gemeint, in dem eine Variable sichtbar und gültig ist. Zum einen gibt es in JavaScript den globalen Scope. Dort liegen alle Variablen, die außerhalb einer Funktion definiert wurden.

Definiert man eine Funktion, gilt innerhalb dieser Funktion ein neuer Scope. Die Parameter der Funktion und alle Variablen, die innerhalb der Funktion definiert werden sind nur in diesem Scope sichtbar. Der Scope, in dem die Funktion definiert wurde, gilt diesem neuen Scope als übergeordnet.

In einem Scope können beliebige neue Variablen angelegt werden, auch solche, deren Name in einem übergeordneten Scope bereits genutzt wurde. Sie werden dann von der Variablen im untergeordneten Scope verdeckt. Wird eine Variable genutzt, die im inneren Scope nicht definiert wurde, wird auf die Variable im äußeren Scope zugegriffen.

Beispiel
var bar = 1;
var baz = 17;
var foo = function () {
    var baz = 2;             // Erzeugt eine neue Variable im inneren Scope
    var zap = 9;             // Noch eine neue Variable
    return bar + baz + zap;
  };
foo();                       // Gibt 12 zurück
bar + zap;                   // Erzeugt einen Ausführungsfehler, da zap nicht definiert wurde.

Es ist auch möglich, Funktionen innerhalb anderer Funktionen zu definieren. Sie sind dann Teil des Scopes der enthaltenden Funktion. Für den Code in der inneren Funktion gilt, dass er auf die Variablen der inneren Funktion, die Variablen der äußeren Funktion und auf die globalen Variablen zugreifen kann. Natürlich kann man diese Schachtelung beliebig vertiefen, es entstehen dann entsprechend lange Scope-Ketten.

Beispiel
var bar = 1;
var baz = 17;
var bereche = function (c) {
    var helper = function(a, b) {     // Geschachtelte Funktion, kann auf c zugreifen
        return a*(b+c);
    };
    var ergebnis = helper(2, 7);      // Erzeugt eine neue Variable im inneren Scope
    return ergebnis;
};
berechne(17);                         // Gibt 48 zurück
helper(1,2);                          // Fehler, helper ist hier nicht definiert

Zu beachten ist hier, dass Variablen, die mit var definiert wurden, an den Anfang der Funktion gehoben werden (hoisting), in der sie definiert wurden. Sie können also nicht lokal zu einem Anweisungsblock definiert werden, der durch geschweifte Klammern markiert wird. Dieses Verhalten weicht von dem anderer Programmiersprachen ab, und man hat mit ECMAScript 2015 neue Schlüsselwörter (let und const) eingeführt, deren Scope auf den Anweisungsblock begrenzt ist, in dem sie notiert werden, so dass außer dem Funktions-Scope nun noch ein Block-Scope in JavaScript hinzukommt.

Dadurch, dass eine Funktion auf den Scope der Funktion zugreifen kann, innerhalb der sie definiert wird, und dass Funktionen in JavaScript Werte sind, die auch übergeben und zurückgegeben werden können, entstehen Effekte, die zu arger Verwirrung führen, aber auch sehr nützlich sein können. Betrachten Sie folgendes Beispiel:

Beispiel
function erzeugeAddierer(konstante) {
    var addiereWert = function(a) {
       return a+konstante;
    };
    return addiereWert;
};
 
var add2 = erzeugeAddierer(2),
    add7 = erzeugeAddierer(7);
add2(7);                              // gibt 9 zurück!
add7(9);                              // gibt 16 zurück!

Die Funktion erzeugeAddierer definiert eine innere Funktion addiereWert. Diese innere Funktion greift auf den Parameter konstante von erzeugeAddierer zu. Danach wird sie als Wert zurückgegeben. Damit dieser Zugriff überhaupt möglich ist, muss JavaScript die zu diesem Zeitpunkt gültige Scope-Kette aufbewahren und an den zurückgegebenen Wert binden. Man nennt dies eine Closure, die zurückgegebene Funktion schließt die Scope-Kette mit ein, die während ihrer Definition galt. Deswegen kann add2(7) auf das Argument 2, das beim Aufruf von erzeugeAddierer(2) galt und das nach Ende dieser Funktion eigentlich gar nicht mehr gültig ist, noch zugreifen.

Es wird noch komplizierter dadurch, dass erzeugeAddierer mehrfach aufgerufen werden kann. Bei jedem Aufruf wird eine Funktion addiereWert definiert, und jede davon bekommt ihre eigene Scope-Kette. Deswegen addiert add7() den Wert 7 und add2() den Wert 2.

Es sei davor gewarnt, dies zu ausgiebig zu nutzen. Scopes belegen Speicher, die Variablen eines Scopes belegen Speicher und wenn ein Scope per Closure an einer langlebenden Funktionsinstanz hängt, kann das dazu führen, dass Speicher nicht freigegeben wird der eigentlich schon längst nicht mehr genutzt wird.

Weitere Feinheiten zu Scopes in JavaScript finden Sie im Tutorial zum Umgang mit Callback-Funktionen und im Tutorial zu Namensräumen.

[Bearbeiten] arguments

arguments ist innerhalb von Funktionen eine System-Variable, die sich ähnlich wie ein Array verhält.
  • Achtung

Sie hat eine Eigenschaft length, der die Anzahl der übergebenen Argumente entnommen werden kann. Zugriff auf die Werte der einzelnen Argumente erhält man über arguments[0] bis arguments[arguments.length - 1], dies sind die der Funktion übergebenen Werte in genau dieser Reihenfolge.

Ähnlich wie ein Array, aber nicht genauso, da arguments jene Methoden, die ein Array hat (z.B. splice()), nicht besitzt. Um diese dennoch anwenden zu können, kann man sich mittels .call() bzw. .apply() behelfen.

Beispiel
// foo verwendet die Arraymethode splice, um das erste übergebene Argument abzuschneiden, 
// und gibt dieses neue Array zurück.
 
var foo = function () {
    return [].splice.call(arguments, 1);
  };
foo(1, 2, 3, 4); // Gibt ein Array [2, 3, 4] zurück.
Beachten Sie: Es ist prinzipiell eine schlechte Idee und davon abzuraten, ein Argument oder eine Variable mit dem Namen "arguments" zu erstellen.

[Bearbeiten] Siehe auch

[Bearbeiten] Quellen


Meine Werkzeuge
Namensräume

Varianten
Aktionen
Übersicht
Index
Mitmachen
Werkzeuge
Spenden
SELFHTML