Ganz kleines JavaScript-Programm
bearbeitet von OrlokHallo Julia
> Ich habe folgendes Programm und muss die Ausgabe bestimmen:
>
> ~~~JavaScript
> function f1(x) {
> return y => x * y;
> };
> const f3 = f1(3);
> const f5 = f1(5);
>
> alert(f3(11) + " " + f5(7));
> ~~~
>
>
> Ausgabe: 33 35
>
> Wie kommt es zu dieser Ausgabe?
Wie schon richtig angemerkt wurde, hast du in `f1` eine Funktion, die eine Funktion als Ergebnis zurückgibt. Genauer gesagt wird eine anonyme Funktion zurückgegeben, für die bei jedem Aufruf von `f1` ein neues Funktionsobjekt erstellt wird. Es ist wichtig für das Verständnis, dass hier immer eine *neue* Funktion erzeugt und zurückgegeben wird:
~~~JavaScript
function f1(x) {
return y => x * y;
}
f1(3) === f1(3); // false
~~~
Funktionen, die andere Funktionen als Argumente entgegennehmen oder die wie in deinem Beispiel eine Funktion als Wert zurückgeben, nennt man *Funktionen höherer Ordnung*. In Sprachen wie JavaScript, die es erlauben mit Funktionen im Wesentlichen so zu verfahren wie mit anderen Werten auch, die es also zum Beispiel erlauben Funktionen in Variablen zu hinterlegen oder sie wie hier innerhalb einer Rückgabeanweisung zu definieren, werden solche Konstrukte häufig genutzt um über bestimmte Abläufe zu abstrahieren.
In diesem Fall hast du originär eine binäre Operation, nämlich die skalare Multiplikation wie man sie aus der Schulmathematik kennt. Wolltest du hier mehrfach mit demselben Faktor multiplizieren, müsstest du diesen grundsätzlich immer angeben:
~~~JavaScript
2 * 5; // 10
2 * 7; // 14
2 * 8; // 16
~~~
Das wird schnell repetitiv und mit jeder Wiederholung erhöht sich die Wahrscheinlichkeit, dass du einen Fehler einbaust. Bist du also in der Situation, öfter mit einem bestimmten Wert multiplizieren zu müssen, bietet es sich an, diese Aufgabe in eine eigene Funktion auszulagern. Das könnte dann etwa so aussehen:
~~~JavaScript
function multiply2(x) {
return 2 * x;
}
multiply2(5); // 10
~~~
Das sieht schon besser aus, aber was, wenn es auch mehrere Stellen gibt, an denen du den Wert `3` als Faktor verwenden willst, schreibst du dir dann eine Funktion `multiply3`? Hier merkst du, dass du eigentlich eine Funktion möchtest, die es dir erlaubt, einen beliebigen Faktor festzusetzen, die also über die Aufgabe „multipliziere mit einer bestimmten Zahl“ abstrahiert. Das Ergebnis dieser Überlegungen ist dann deine Funktion `f1`.
Aber wie funktioniert das Festlegen des einen Operanden genau?
Stellen wir uns kurz den allgemeinen Ablauf vor: Du rufst die Funktion `f1` mit einem Wert auf, sagen wir `5`. Wird die Kontrolle vom aufrufenden Kontext an die aufgerufene Funktion `f1` übergeben, dann wird der Wert `5` an den formalen Parameter `x` gebunden. Damit wird aber zunächst gar nichts gemacht. Die einzige Anweisung der Funktion besteht darin, eine anonyme Funktion zu erzeugen und sie an den Aufrufer zurückzugeben. Interessant wird es erst, wenn nun diese von `f1` zurückgegebene einstellige Funktion aufgerufen wird:
~~~JavaScript
function f1(x) {
return y => x * y;
}
const f5 = f1(5);
f5(3); // 15
~~~
Der Parameter `y` der anonymen Funktion wird hier mit dem Wert `3` initialisiert, das heißt, in dem arithmetischen Ausdruck wird `y` durch `3` ersetzt. Nun steht da aber auch noch ein `x`, und ein solches ist innerhalb der Funktion, die jetzt `f5` heißt, nicht definiert.
Nun ist es aber so, dass Funktionen in gewisser Weise über ein Gedächtnis verfügen. Sie merken sich, in welcher Umgebung sie definiert wurden. Das bedeutet, auch wenn Funktionen wie oben beschrieben *Bürger erster Klasse* sind und beliebig zwischen verschiedenen Teilen eines Programms hin- und hergeschoben werden können, können sie weiterhin auf Variablen zugreifen, die zum Zeitpunkt ihrer Erzeugung für die Funktion sichtbar waren.
Als die Funktion `f5` erzeugt wurde, existierte in ihrer lexikalischen Umgebung, nämlich dem Gültigkeitsbereich, der mit dem Funktionskontext von `f1` assoziiert war, eine Variable namens `x`, die an den Wert `5` gebunden war. (Aus Sicht des Körpers einer Funktion ist ein Parameter nichts anderes als eine lokale Variable.) Diese Variable `x` wurde nach der Abarbeitung von `f1` nicht verworfen, sondern konserviert.
Beim Aufruf von `f5` wird nun zunächst geprüft, ob in `f5` selbst eine Variable bzw. ein Parameter mit dem Bezeichner `x` existiert. Da dies offenbar nicht der Fall ist, wird in dem Gültigkeitsbereich nachgesehen, in dem `f5` definiert wurde. Dort wird eine entsprechende Variable gefunden und deren Wert `5` in den Ausdruck eingesetzt:
~~~JavaScript
const f5 = f1(5); // y => 5 * y
f5(3); // 3 => 5 * 3
~~~
Greift eine Funktion wie hier `f5` auf Variablen oder Funktionen zurück, die in der Umgebung definiert waren, in der die Funktion erzeugt wurde, dann spricht man auch von einem Funktionsabschluss, oder im Englischen: *Closure*. Diese Funktionalität ist Grundlage für Techniken wie *Currying* – die Zerlegung einer mehrstelligen Funktion in mehrere einstellige Funktionen. Dafür ist deine Funktion `f1` ein Beispiel.
Viele Grüße,
Orlok
--
„Dance like it hurts.
Make love like you need money.
Work when people are watching.“ — Dogbert
Ganz kleines JavaScript-Programm
bearbeitet von OrlokHallo Julia
> Ich habe folgendes Programm und muss die Ausgabe bestimmen:
>
> ~~~JavaScript
> function f1(x) {
> return y => x * y;
> };
> const f3 = f1(3);
> const f5 = f1(5);
>
> alert(f3(11) + " " + f5(7));
> ~~~
>
>
> Ausgabe: 33 35
>
> Wie kommt es zu dieser Ausgabe?
Wie schon richtig angemerkt wurde, hast du in `f1` eine Funktion, die eine Funktion als Ergebnis zurückgibt. Genauer gesagt wird eine anonyme Funktion zurückgegeben, für die bei jedem Aufruf von `f1` ein neues Funktionsobjekt erstellt wird. Es ist wichtig für das Verständnis, dass hier immer eine *neue* Funktion erzeugt und zurückgegeben wird:
~~~JavaScript
function f1(x) {
return y => x * y;
}
f1(3) === f1(3); // false
~~~
Funktionen, die andere Funktionen als Argumente entgegennehmen oder die wie in deinem Beispiel eine Funktion als Wert zurückgegeben, nennt man *Funktionen höherer Ordnung*. In Sprachen wie JavaScript, die es erlauben mit Funktionen im Wesentlichen so zu verfahren wie mit anderen Werten auch, die es also zum Beispiel erlauben Funktionen in Variablen zu hinterlegen oder sie wie hier innerhalb einer Rückgabeanweisung zu definieren, werden solche Konstrukte häufig genutzt um über bestimmte Abläufe zu abstrahieren.
In diesem Fall hast du originär eine binäre Operation, nämlich die skalare Multiplikation wie man sie aus der Schulmathematik kennt. Wolltest du hier mehrfach mit demselben Faktor multiplizieren, müsstest du diesen grundsätzlich immer angeben:
~~~JavaScript
2 * 5; // 10
2 * 7; // 14
2 * 8; // 16
~~~
Das wird schnell repetitiv und mit jeder Wiederholung erhöht sich die Wahrscheinlichkeit, dass du einen Fehler einbaust. Bist du also in der Situation, öfter mit einem bestimmten Wert multiplizieren zu müssen, bietet es sich an, diese Aufgabe in eine eigene Funktion auszulagern. Das könnte dann etwa so aussehen:
~~~JavaScript
function multiply2(x) {
return 2 * x;
}
multiply2(5); // 10
~~~
Das sieht schon besser aus, aber was, wenn es auch mehrere Stellen gibt, an denen du den Wert `3` als Faktor verwenden willst, schreibst du dir dann eine Funktion `multiply3`? Hier merkst du, dass du eigentlich eine Funktion möchtest, die es dir erlaubt, einen beliebigen Faktor festzusetzen, die also über die Aufgabe „multipliziere mit einer bestimmten Zahl“ abstrahiert. Das Ergebnis dieser Überlegungen ist dann deine Funktion `f1`.
Aber wie funktioniert das Festlegen des einen Operanden genau?
Stellen wir uns kurz den allgemeinen Ablauf vor: Du rufst die Funktion `f1` mit einem Wert auf, sagen wir `5`. Wird die Kontrolle vom aufrufenden Kontext an die aufgerufene Funktion `f1` übergeben, dann wird der Wert `5` an den formalen Parameter `x` gebunden. Damit wird aber zunächst gar nichts gemacht. Die einzige Anweisung der Funktion besteht darin, eine anonyme Funktion zu erzeugen und sie an den Aufrufer zurückzugeben. Interessant wird es erst, wenn nun diese von `f1` zurückgegebene einstellige Funktion aufgerufen wird:
~~~JavaScript
function f1(x) {
return y => x * y;
}
const f5 = f1(5);
f5(3); // 15
~~~
Der Parameter `y` der anonymen Funktion wird hier mit dem Wert `3` initialisiert, das heißt, in dem arithmetischen Ausdruck wird `y` durch `3` ersetzt. Nun steht da aber auch noch ein `x`, und ein solches ist innerhalb der Funktion, die jetzt `f5` heißt, nicht definiert.
Nun ist es aber so, dass Funktionen in gewisser Weise über ein Gedächtnis verfügen. Sie merken sich, in welcher Umgebung sie definiert wurden. Das bedeutet, auch wenn Funktionen wie oben beschrieben *Bürger erster Klasse* sind und beliebig zwischen verschiedenen Teilen eines Programms hin- und hergeschoben werden können, können sie weiterhin auf Variablen zugreifen, die zum Zeitpunkt ihrer Erzeugung für die Funktion sichtbar waren.
Als die Funktion `f5` erzeugt wurde, existierte in ihrer lexikalischen Umgebung, nämlich dem Gültigkeitsbereich, der mit dem Funktionskontext von `f1` assoziiert war, eine Variable namens `x`, die an den Wert `5` gebunden war. (Aus Sicht des Körpers einer Funktion ist ein Parameter nichts anderes als eine lokale Variable.) Diese Variable `x` wurde nach der Abarbeitung von `f1` nicht verworfen, sondern konserviert.
Beim Aufruf von `f5` wird nun zunächst geprüft, ob in `f5` selbst eine Variable bzw. ein Parameter mit dem Bezeichner `x` existiert. Da dies offenbar nicht der Fall ist, wird in dem Gültigkeitsbereich nachgesehen, in dem `f5` definiert wurde. Dort wird eine entsprechende Variable gefunden und deren Wert `5` in den Ausdruck eingesetzt:
~~~JavaScript
const f5 = f1(5); // y => 5 * y
f5(3); // 3 => 5 * 3
~~~
Greift eine Funktion wie hier `f5` auf Variablen oder Funktionen zurück, die in der Umgebung definiert waren, in der die Funktion erzeugt wurde, dann spricht man auch von einem Funktionsabschluss, oder im Englischen: *Closure*. Diese Funktionalität ist Grundlage für Techniken wie *Currying* – die Zerlegung einer mehrstelligen Funktion in mehrere einstellige Funktionen. Dafür ist deine Funktion `f1` ein Beispiel.
Viele Grüße,
Orlok
--
„Dance like it hurts.
Make love like you need money.
Work when people are watching.“ — Dogbert
Ganz kleines JavaScript-Programm
bearbeitet von OrlokHallo Julia
> Ich habe folgendes Programm und muss die Ausgabe bestimmen:
>
> ~~~JavaScript
> function f1(x) {
> return y => x * y;
> };
> const f3 = f1(3);
> const f5 = f1(5);
>
> alert(f3(11) + " " + f5(7));
> ~~~
>
>
> Ausgabe: 33 35
>
> Wie kommt es zu dieser Ausgabe?
Wie schon richtig angemerkt wurde, hast du in `f1` eine Funktion, die eine Funktion als Ergebnis zurückgibt. Genauer gesagt wird eine anonyme Funktion zurückgegeben, für die bei jedem Aufruf von `f1` ein neues Funktionsobjekt erstellt wird. Es ist wichtig für das Verständnis, dass hier immer eine *neue* Funktion erzeugt und zurückgegeben wird:
~~~JavaScript
function f1(x) {
return y => x * y;
}
f1(3) === f1(3); // false
~~~
Funktionen, die andere Funktionen als Argumente entgegennehmen oder die wie in deinem Beispiel eine Funktion als Wert zurückgegeben, nennt man *Funktionen höherer Ordnung*. In Sprachen wie JavaScript, die es erlauben mit Funktionen im Wesentlichen so zu verfahren wie mit anderen Werten auch, die es also zum Beispiel erlauben Funktionen in Variablen zu hinterlegen oder sie wie hier innerhalb einer Rückgabeanweisung zu definieren, werden solche Konstrukte häufig genutzt um über bestimmte Abläufe zu abstrahieren.
In diesem Fall hast du originär eine binäre Operation, nämlich die skalare Multiplikation wie man sie aus der Schulmathematik kennt. Wolltest du hier mehrfach mit demselben Faktor multiplizieren, müsstest du diesen grundsätzlich immer angeben:
~~~JavaScript
2 * 5; // 10
2 * 7; // 14
2 * 8; // 16
~~~
Das wird schnell repetitiv und mit jeder Wiederholung erhöht sich die Wahrscheinlichkeit, dass du einen Fehler einbaust. Bist du also in der Situation, öfter mit einem bestimmten Wert multiplizieren zu müssen, bietet es sich an, diese Aufgabe in eine eigene Funktion auszulagern. Das könnte dann etwa so aussehen:
~~~JavaScript
function multiply2(x) {
return 2 * x;
}
multiply2(5); // 10
~~~
Das sieht schon besser aus, aber was, wenn es auch mehrere Stellen gibt, an denen du den Wert `3` als Faktor verwenden willst, schreibst du dir dann eine Funktion `multiply3`? Hier merkst du, dass du eigentlich eine Funktion möchtest, die es dir erlaubt, einen beliebigen Faktor festzusetzen, die also über die Aufgabe „multipliziere mit einer bestimmten Zahl“ abstrahiert. Das Ergebnis dieser Überlegungen ist dann deine Funktion `f1`.
Aber wie funktioniert das Festlegen des einen Operanden genau?
Stellen wir uns kurz den allgemeinen Ablauf vor: Du rufst die Funktion `f1` mit einem Wert auf, sagen wir `5`. Wird die Kontrolle vom aufrufenden Kontext an die aufgerufene Funktion `f1` übergeben, dann wird der Wert `5` an den formalen Parameter `x` gebunden. Damit wird aber zunächst gar nichts gemacht. Die einzige Anweisung der Funktion besteht darin, eine anonyme Funktion zu erzeugen und sie an den Aufrufer zurückzugeben. Interessant wird es erst, wenn nun diese von `f1` zurückgegebene einstellige Funktion aufgerufen wird:
~~~JavaScript
function f1(x) {
return y => x * y;
}
const f5 = f1(5);
f5(3); // 15
~~~
Der Parameter `y` der anonymen Funktion wird hier mit dem Wert `3` initialisiert, das heißt, in dem arithmetischen Ausdruck wird `y` durch `3` ersetzt. Nun steht da aber auch noch ein `x`, und ein solches ist innerhalb der Funktion, die jetzt `f5` heißt, nicht definiert.
Nun ist es aber so, dass Funktionen in gewisser Weise über ein Gedächtnis verfügen. Sie merken sich, in welcher Umgebung sie definiert wurden. Das bedeutet, auch wenn Funktionen wie oben beschrieben *Bürger erster Klasse* sind und beliebig zwischen verschiedenen Teilen eines Programms hin- und hergeschoben werden können, können sie weiterhin auf Variablen zugreifen, die zum Zeitpunkt ihrer Erzeugung für die Funktion sichtbar waren.
Als die Funktion `f3` erzeugt wurde, existierte in ihrer lexikalischen Umgebung, nämlich dem Gültigkeitsbereich, der mit dem Funktionskontext von `f1` assoziiert war, eine Variable namens `x`, die an den Wert `5` gebunden war. (Aus Sicht des Körpers einer Funktion ist ein Parameter nichts anderes als eine lokale Variable.) Diese Variable `x` wurde nach der Abarbeitung von `f1` nicht verworfen, sondern konserviert.
Beim Aufruf von `f5` wird nun zunächst geprüft, ob in `f5` selbst eine Variable bzw. ein Parameter mit dem Bezeichner `x` existiert. Da dies offenbar nicht der Fall ist, wird in dem Gültigkeitsbereich nachgesehen, in dem `f5` definiert wurde. Dort wird eine entsprechende Variable gefunden und deren Wert `5` in den Ausdruck eingesetzt:
~~~JavaScript
const f5 = f1(5); // y => 5 * y
f5(3); // 3 => 5 * 3
~~~
Greift eine Funktion wie hier `f5` auf Variablen oder Funktionen zurück, die in der Umgebung definiert waren, in der die Funktion erzeugt wurde, dann spricht man auch von einem Funktionsabschluss, oder im Englischen: *Closure*. Diese Funktionalität ist Grundlage für Techniken wie *Currying* – die Zerlegung einer mehrstelligen Funktion in mehrere einstellige Funktionen. Dafür ist deine Funktion `f1` ein Beispiel.
Viele Grüße,
Orlok
--
„Dance like it hurts.
Make love like you need money.
Work when people are watching.“ — Dogbert