Parameterdeklarationen in Funktionsausdrücken in JavaScript
Die Schreibweise » x =>« kann wie »()=>« verwendet werden, ergibt aber eine Funktion mit einem Parameter, zu deren Aufruf ein Argument nötig ist.
- Ein Funktionsausdruck mit einem Parameter
Ausdruck
.------------. .--. .----------.
--->| Bezeichner |ooo>( => )--->| Ausdruck |--->
'------------' '--' '----------'
Zwischen dem Bezeichner und dem Pfeilsymbol »=>« darf die Zeile nicht beendet werden.
- Auswertung
x => 1
function ()
- Auswertung
( x => 1 )( 2 )
1
Im inneren Ausdruck des Funktionsausdrucks (rechts vom Pfeil »=>«) steht der Name »x« für den Wert des Arguments.
- Auswertung
( x => x )( 2 )
2
Der Wert des Parameters »x« in der obigen Anwendung der Pfeilfunktion beträgt «2». Aber dieser Wert wird nicht dauerhaft, wie ein globaler Eintrag, gespeichert, sondern nur vorübergehend während der Anwendung der Pfeilfunktion auf einen Argumentwert. Dazu wird vorübergehend eine Kopie der Pfeilfunktion erstellt, in welcher der Parametername an den Argumentwert gebunden ist. Von außerhalb der Pfeilfunktion kann nicht direkt auf den Wert des Parameters der Pfeilfunktion zugegriffen weren.
- Auswertung
( x => 1 + x )( 2 )
3
- Auswertung
( x => x + x )( 4 + 2 )
12
Der Parameter muß nicht immer den Namen »x« haben. Auch andere Namen, wie »o« oder »Wert« sind möglich, solange sie nicht mit anderen, schon verwendeten Namen in Konflikt geraten. (Dies wird auch als „Alpha-Äquivalenz “ bezeichnet.)
- Auswertung
( o => 1 + o )( 2 )
3
- Auswertung
( Wert => 1 + Wert )( 2 )
3
Etwas, das einen Parameter hat, nennt man – wie schon früher einmal erwähnt – manchmal auch „parametrisiert “. Wir sahen oben also parametrisierte Pfeilfunktionen. Etwas, das keinen Parameter hat, nennt man – wie schon früher einmal erwähnt – manchmal auch „parameterlos “.
Ein Parameter ist keine Eigenschaft des globalen Objektes, sondern ein davon unabhängiger Name für einen Wert (für den Wert des Arguments, mit dem die Funktion aufgerufen wurde).
Deklarationen
Wenn ein Name an einer Stelle verwendet wird, die extra dafür gedacht ist, diesen Namen für einen bestimmten Zweck einzuführen, so nennt man dies auch eine Deklaration des Namens. In »x => 1« wird also ein Parameter »x« deklariert.
- Auswertung
x => 1
function ()
Bezeichner
Als Name in einer Deklaration (hier also als Parameternamen) sind fast alle Bezeichnernamen erlaubt. (Der Begriff wurde schon vorgestellt, es handelt sich vereinfacht gesagt um eine Folge von Buchstaben, die ab der zweiten Position auch Ziffern enthalten darf.)
Genauer gesagt, muß der Name ein Bezeichner sein. Wir hatten diesen Begriff auch schon eingeführt, aber damals den Unterschied zu einem Bezeichnernamen noch nicht behandelt.
Ein Bezeichner ist ein Bezeichnername, der kein reserviertes Wort ist.
Reservierte Wörter, wie »in« (ein schon vorgestellter Operator), dürfen nicht als Namen von Konstanten verwendet werden, obwohl sie als Namen von Eigenschaften verwendet werden können.
- Konsole
in => 0
SyntaxError: expected expression, got keyword 'in'
- Konsole
x => 0
function ()
Hier listen wir einige Wörter auf, die nicht als Konstante deklariert werden dürfen. Es handelt sich dabei hauptsächlich um reservierte Wörter. (Weitere Details findet man in ECMAScript 2016, 11.6.2 und 12.1.)
- Schlüsselwörter
- break do in typeof case else instanceof var catch export new void class extends return while const finally super with continue for switch yield debugger function this default if throw delete import try
- zukünftige Schlüsselwörter
- enum await
- nicht erlaubte Literale
- null true false
Benennung parametrisierter Funktionen
Parametrisierte Funktionen können wie nicht-parametrisierte Funktion auch benannt werden.
- Auswertung
f = x => typeof x
function f()
- Auswertung
f( 2 )
"number"
- Auswertung
f( "2" )
"string"
Fehlende Argumente
Wird für ein Parameter kein Argumentwert angegeben, so erhält der Parameter den Wert «undefined».
- Auswertung
( x => x )()
undefined
(Dies kann verwendet werden, um den Wert «undefined» zu erhalten, selbst wenn »this.undefined« umdefiniert worden sein könnte.)
Wird «undefined» in Rechnungen verwendet, kann sich dann beispielsweise der Wert «NaN» ergeben.
- Auswertung
( x => 1 + x )()
NaN
Überzählige Argumente
Wird ein Argumentwert angegeben, obwohl eine Funktion gar keinen Parameter hat, wird der Argumentwert ignoriert.
- Auswertung
( ()=> 2 )( 3 )
2
Das überzählige Argument wird ausgewertet, obwohl der sich ergebende Wert ignoriert wird.
- Auswertung
( ()=> 2 )( console.log( "abc" ))
abc
2
Auch mehrere überzählige Argumente werden ausgewertet.
- Auswertung
( ()=> console.log( "ghi" ))( console.log( "abc" ), console.log( "def" ))
abc
def
ghi
undefined
Lokale Namen
Parameter sind lokale Namen und keine globalen Einträge. Sie können nur innerhalb der definierten Funktion verwendet werden. Sie gehören zu der „lexikalischen Umgebung“ dieser Funktion, welche aber kein Verzeichnis ist, auf das man direkt zugreifen kann.
Für globale Einträge verwenden wir in diesem Kurs bevorzugt kurze Namen, weil diese meist noch unbelegt sind. Für lokale Namen, wie Parameter, können aber ohne Bedenken auch längere Namen verwendet werden.
- Auswertung
f = wertDessenTypErmitteltWerdenSoll => typeof wertDessenTypErmitteltWerdenSoll
function f()
Parameter verdecken globale Einträge
Wenn es sowohl einen Parameter als auch einen globalen Eintrag mit dem gleichen Namen gibt, dann steht jener Name innerhalb einer Funktion (rechts vom Pfeil »=>«) für den Parameter.
- Zuweisung an eine globale Eigenschaft
this.x = 1
undefined
- Auswertung
f = x => x
function f()
- Auswertung
f( 2 )
2
Um den globalen Eintrag zu erreichen kann eine Qualifikation mit »this« verwendet werden.
- Auswertung
f = x => this.x
function f()
- Auswertung
f( 2 )
1
Das defensive »window.« ⃗
Wenn (versehentlich oder absichtlich) »alert« als Parametername verwendet wird, dann hat »alert( 22 );« (rechts vom Pfeil »=>«) nicht mehr unbedingt die beabsichtigte Wirkung, während »window.alert« von der Umdefinition unbeeinflußt bleibt.
In der folgende Folge von Auswertungen öffnet der Aufruf »f( x=>x )« kein Meldungsfenster mehr, während »g( x=>x )« weiterhin ein Meldungsfenster öffnet.
- Auswertung
f = alert => alert( 22 );
function f()
- Auswertung
g = alert => window.alert( 24 );
function g()
- Auswertung
f( x=>x )
22
- Auswertung
g( x=>x )
- [24]
undefined
Es könnte zwar auch sein, daß jemand »window« umdefiniert, doch ist »window« so prominent, daß dies weniger wahrscheinlich ist als eine Umdefinition von »alert«. Deswegen ist es vermutlich etwas sicherer »window.alert« statt »alert« zu verwenden.
Berechnete Namen ⃗
Wir können nun eine Funktion »d« schreiben, die Zahlen in deutsche Wörter übersetzt.
- Auswertung
d0 = "Null"
"Null"
- Auswertung
d1 = "Eins"
"Eins"
- Auswertung
d = x => eval( "d" + x )
function d()
- Auswertung
d( 0 )
"Null"
- Auswertung
d( 1 )
"Eins"
Die hier gezeigt Verwendung von »eval« gilt in JavaScript eigentlich nicht als guter Stil, da es für das Anlegen solcher Zuordnungen in JavaScript auch Möglichkeiten ohne »eval« gibt, die dafür bevorzugt eingesetzt werden sollten. Weil wir jene Möglichkeiten aber bisher noch nicht kennengelernt haben, können wir vorübergehend »eval« für solche Zwecke einsetzen. Außerdem ist das obige Beispiel zum Verständnis der Sprache hilfreich – auch wenn wir später noch bessere Lösungen für Zuordnungen von Texten zu Zahlen kennenlernen werden.
Beispiel Eine Funktion, die ergibt, ob ihr Argumenwert »NaN« ist ⃗
- Auswertung
is_NaN = x => x !== x
function is_NaN()
- Auswertung
is_NaN( 2 )
false
- Auswertung
is_NaN( NaN )
true
Blockfunktionen mit Parametern ⃗
Etwaige Parameter der Funktion können in dem Block verwendet werden.
- Auswertung
f = x =>{ console.log( "abc" ); console.log( x ); }
function f()
- Auswertung
f( "012" )
abc
012
undefined
Zuweisungen an Parameter ⃗
Einem Parameter einer Funktion kann innerhalb dieser Funktion auch ein Wert zugewiesen werden.
- Auswertung
f = x =>{ x = x + 2; console.log( x ); x = x + 2; console.log( x ); }
function f()
f( 3 )
5
7
Wir hatten früher einen Parameter als einen Namen für einen Wert bezeichnet. Da wird jetzt aber sehen, daß der Wert eines Parameter auch durch eine Zuweisung verändert werden kann, müssen wir den Parameter jetzt als Name für einen Speicherplatz ansehen, der zunächst den Wert des Arguments erhält, aber dem dann innerhalb der Funktion auch ein anderer Wert zugewiesen werden kann.
Schleifen ⃗
main.js
VierMal = f =>{ f(); f(); f(); f(); }
VierMal( () => print( "Hallo!" ) )
- Protokoll
Hallo!
Hallo!
Hallo!
Hallo!
Rekursion ⃗
Bereits innerhalb einer Funktion kann der interne Name der Funktion (also ihr name-Eintrag) verwendet werden, damit die Funktion sich selbst aufrufen kann. Diese sogenannte Rekursion könnte den Browser allerdings blockieren, da die Funktion sich ohne Ende immer wieder selber aufruft. Wir werden erst später lernen, wie solch eine Rekursion besser gesteuert werden kann.
- Auswertung
f = x =>{ x = x + 2; console.log( x ); f( x ); }
function f()
f.name
"f"
f( 3 )
5
7
9
11
- …
InternalError: too much recursion
Durch die Rekursion wird eine neue Inkarnation der Funktion angelegt, während eine Inkarnation bereits aktiv ist. Dies geht immer so weiter, und jede neue Inkarnation benötigt unter vielen JavaScript -Implementation eigenen Speicherplatz. Daher ist nach einer Weile kein Speicherplatz mehr vorhanden, und die Rekursion bricht dann doch ab.
Das folgende Konsolenprotokoll zeigt, wie die Rekursion doch mit unseren Mitteln auf 10 Schritte begrenzt werden könnte.
- Konsole
f0 = x =>{ x = x + 1; console.log( x ); eval( "f" + Math.floor( x/10 )+ "( x )" ); }
function f0()
f1 = x =>{ }
function f1()
f0( 0 )
1
2
3
4
5
6
7
8
9
10
undefined
Unmittelbare Aufrufe von Funktionen *
Ein Programmteil, der einen Wert vorübergehend unter einem globalen Schlüssel, wie »x«, abspeichern will, kann nicht sicher sein, ob dieser Schlüssel nicht schon von einem anderen Programmteil verwendet wird.
Statt dessen kann auch ein Parameter einer Funktion verwendet werden, die mit einem Pfeil angegeben und sofort aufgerufen wird.
- Auswertung
( x =>{ x = 2; console.log( x + 3 ); } )()
5
Der globale Schlüssel »x« ist nach der obenstehenden Auswertung weiterhin undefiniert, weil der Name »x« im obigen Block für den nur vorübergehend angelegten Parameter »x« steht.
- Auswertung
x
ReferenceError: x is not defined
Obwohl Parameter in der Praxis selten als Variablen verwendet werden, kommt der sofortige Aufruf von angegebenen Funktionen in JavaScript bei fortgeschrittenen Programmierern relativ häufig vor.
Rückgabe einer neuen Funktion *
Eine Funktion kann auch eine Funktion als Ergebnis haben.
Die Funktion »plus« ergibt eine Funktion, welche eine Zahl »x« um den als Argument beim Aufruf von »inc« angegebenen Wert »a« erhöht und dann ausgibt.
- Auswertung
plus = a => x => { console.log( x + a ); };
function inc()
- Auswertung
plus3 = inc( 3 );
function inc/<()
Die Funktion »plus3« ist nun »x => { console.log( x + 3 ); }«.
- Auswertung
plus3( 8 )
11
- Auswertung
plus2 = inc( 2 );
function inc/<()
Die Funktion »plus2« ist nun »x => { console.log( x + 2 ); }«.
- Auswertung
plus2( 8 )
10
Wir können erkennen, daß »plus3« und »plus2« zwei verschiedene Funktionen sind.
- Auswertung
plus3 === plus2
false
Die Funktion »plus« erzeugt also bei jedem Aufruf eine neue Funktion.
Ausdrücke für bestimmte Werte ⃗
In älteren JavaScript -Implementationen konnte man globale Einträge, wie »this.Infinity« und »this.undefined« umdefinieren. Wir zeigen hier Ausdrücke, die auch dann verwendet werden könne, um den Wert »Infinity« beziehungsweise »undefined« auszudrücken.
- Ausdruck mit dem Wert »Infinity«
(1/0)
- Ausdruck mit dem Wert »undefined«
((x=>x)())
- Ausdruck mit dem Wert »NaN«
(+"a")
Übungsfragen
? Übungsfrage
Warum hat der Ausdruck »((x=>x)())« den Wert »undefined«?
? Übungsfragen
Können Sie die Werte der folgenden Ausdrücke vorhersagen?
- Ausdruck
( x => 4 )( 3 )
- Ausdruck 1
( x => x )( 9 )
- Ausdruck 2
( o => o )( 9 )
- Ausdruck 3
( x => 2 + x )( 3 )
- Ausdruck 4
( x => x / 2 )( 8 )
- Ausdruck 5
( x => x + x )( 10 + 2 )
- Ausdruck 6 *
( x => y => x - y )( 5 )( 8 )
- Ausdruck 7 *
( f => f( 2 ))( x => x * x )
- Ausdruck 8 *
( f => x => f( x ))( x => x * x )( 3 )
? Übungsfrage
Welchen Wert hat der Aufruf »g()« am Ende der folgenden Eingaben?
- Eingaben
g = ()=> 2 + f( 3 )
f = x => 10 * x
g()
Übungsaufgaben
/ Übungsaufgabe
Schreiben Sie eine Funktion namens »f«, deren Ergebnis um «1» größer ist als der Wert ihres Arguments.
Die Lösung sollte aussehen wie »f = x => …«, wobei dann an Stelle der Ellipse noch etwas einzusetzen ist.
Wenn die Funktion richtig definiert wurde, sollten danach folgende Auswertungen beobachtbar sein.
- Auswertung
f( 2 )
3
- Auswertung
f( 10 )
11
/ Übungsaufgabe
Schreiben Sie eine Funktion »q«, die das Quadrat einer Zahl berechnet.
- Auswertung
q( 2 )
4
- Auswertung
q( 3 )
9
/ Übungsaufgabe
Schreiben Sie eine Funktion »c«, die als Abkürzung von »console.log« verwendet werden kann.
/ Übungsaufgabe
Definieren Sie eine Wirkfunktion »f«, die den ihr übergebenen Argumentwert und den um Eins vermehrten Argumentwert mit »console.log« auf insgesamt zwei Zeilen ausgibt.
- Auswertung
f( 14 )
14
15
undefined