In JavaScript ist eine Funktion keine „magische Sprachstruktur“, sondern eine besondere Art von Wert.
Die Syntax, die wir zuvor verwendet haben, wird Funktionsdeklaration genannt:
function sayHi() { alert( "Hello" );}
Es gibt eine andere Syntax zum Erstellen einer Funktion, die Funktionsausdruck genannt wird.
Sie sieht so aus:
let sayHi = function() { alert( "Hello" );};
Hier wird die Funktion explizit erstellt und der Variablen zugewiesen, wie jeder andere Wert. Egal wie die Funktion definiert ist, sie ist einfach ein Wert, der in der Variablen sayHi
gespeichert wird.
Die Bedeutung dieser Codebeispiele ist die gleiche: „Erstelle eine Funktion und lege sie in die Variable sayHi
„.
Wir können diesen Wert sogar ausdrucken, indem wir alert
verwenden:
function sayHi() { alert( "Hello" );}alert( sayHi ); // shows the function code
Bitte beachten Sie, dass die letzte Zeile die Funktion nicht ausführt, weil nach sayHi
keine Klammern stehen. Es gibt Programmiersprachen, bei denen jede Erwähnung eines Funktionsnamens zu dessen Ausführung führt, aber bei JavaScript ist das nicht der Fall.
In JavaScript ist eine Funktion ein Wert, also können wir sie wie einen Wert behandeln. Der obige Code zeigt seine String-Repräsentation, die der Quellcode ist.
Sicherlich ist eine Funktion ein spezieller Wert, in dem Sinne, dass wir sie wie sayHi()
aufrufen können.
Aber es ist immer noch ein Wert. Also können wir mit ihr arbeiten wie mit anderen Arten von Werten.
Wir können eine Funktion in eine andere Variable kopieren:
function sayHi() { // (1) create alert( "Hello" );}let func = sayHi; // (2) copyfunc(); // Hello // (3) run the copy (it works)!sayHi(); // Hello // this still works too (why wouldn't it)
Hier ist, was oben im Detail passiert:
- Die Funktionsdeklaration
(1)
erstellt die Funktion und setzt sie in die Variable namenssayHi
. - Zeile
(2)
kopiert sie in die Variablefunc
. Bitte beachten Sie noch einmal: NachsayHi
stehen keine Klammern. Wäre das der Fall, dann würdefunc = sayHi()
das Ergebnis des AufrufssayHi()
infunc
schreiben, nicht die FunktionsayHi
selbst. - Jetzt kann die Funktion sowohl als
sayHi()
als auch alsfunc()
aufgerufen werden.
Beachten Sie, dass wir auch einen Funktionsausdruck hätten verwenden können, um sayHi
in der ersten Zeile zu deklarieren:
let sayHi = function() { alert( "Hello" );};let func = sayHi;// ...
Alles würde genauso funktionieren.
Sie fragen sich vielleicht, warum der Funktionsausdruck ein Semikolon ;
am Ende hat, die Funktionsdeklaration aber nicht:
function sayHi() { // ...}let sayHi = function() { // ...};
Die Antwort ist einfach:
- Es besteht keine Notwendigkeit für
;
am Ende von Codeblöcken und Syntaxstrukturen, die sie verwenden, wieif { ... }
,for { }
,function f { }
usw. - Ein Funktionsausdruck wird innerhalb der Anweisung verwendet:
let sayHi = ...;
, als ein Wert. Es handelt sich nicht um einen Codeblock, sondern um eine Zuweisung. Das Semikolon;
wird am Ende von Anweisungen empfohlen, unabhängig davon, welchen Wert sie haben. Das Semikolon bezieht sich hier also nicht auf den Funktionsausdruck selbst, es beendet nur die Anweisung.
Rückruffunktionen
Schauen wir uns weitere Beispiele für die Übergabe von Funktionen als Werte und die Verwendung von Funktionsausdrücken an.
Wir schreiben eine Funktion ask(question, yes, no)
mit drei Parametern:
question
Text der Frage yes
Funktion, die ausgeführt werden soll, wenn die Antwort „Ja“ lautet no
Funktion, die ausgeführt werden soll, wenn die Antwort „Nein“ lautet
Die Funktion soll die question
abfragen und, je nach Antwort des Benutzers, yes()
oder no()
aufrufen:
function ask(question, yes, no) { if (confirm(question)) yes() else no();}function showOk() { alert( "You agreed." );}function showCancel() { alert( "You canceled the execution." );}// usage: functions showOk, showCancel are passed as arguments to askask("Do you agree?", showOk, showCancel);
In der Praxis sind solche Funktionen recht nützlich. Der Hauptunterschied zwischen einer realen ask
und dem obigen Beispiel besteht darin, dass reale Funktionen komplexere Möglichkeiten zur Interaktion mit dem Benutzer nutzen als eine einfache confirm
. Im Browser zeichnet eine solche Funktion normalerweise ein hübsches Fragefenster. Aber das ist eine andere Geschichte.
Die Argumente showOk
und showCancel
von ask
werden Callback-Funktionen oder einfach Callbacks genannt.
Die Idee ist, dass wir eine Funktion übergeben und erwarten, dass sie später „zurückgerufen“ wird, falls nötig. In unserem Fall wird showOk
zum Rückruf für die Antwort „Ja“ und showCancel
für die Antwort „Nein“.
Wir können Funktionsausdrücke verwenden, um die gleiche Funktion viel kürzer zu schreiben:
function ask(question, yes, no) { if (confirm(question)) yes() else no();}ask( "Do you agree?", function() { alert("You agreed."); }, function() { alert("You canceled the execution."); });
Hier werden Funktionen direkt innerhalb des ask(...)
-Aufrufs deklariert. Sie haben keinen Namen und werden daher anonym genannt. Solche Funktionen sind außerhalb von ask
nicht zugänglich (weil sie keinen Variablen zugewiesen sind), aber das ist genau das, was wir hier wollen.
Solcher Code taucht in unseren Skripten ganz natürlich auf, er entspricht dem Geist von JavaScript.
Reguläre Werte wie Strings oder Zahlen repräsentieren die Daten.
Eine Funktion kann als eine Aktion wahrgenommen werden.
Wir können sie zwischen Variablen übergeben und ausführen, wann wir wollen.
Funktionsausdruck vs. Funktionsdeklaration
Lassen Sie uns die Hauptunterschiede zwischen Funktionsdeklarationen und Ausdrücken formulieren.
Erstens, die Syntax: wie man sie im Code unterscheidet.
-
Funktionsdeklaration: eine Funktion, die als separate Anweisung im Hauptcodefluss deklariert wird.
// Function Declarationfunction sum(a, b) { return a + b;}
-
Funktionsausdruck: eine Funktion, die innerhalb eines Ausdrucks oder eines anderen Syntaxkonstrukts erstellt wird. Hier wird die Funktion auf der rechten Seite des „Zuweisungsausdrucks“
=
:// Function Expressionlet sum = function(a, b) { return a + b;};
Der feinere Unterschied besteht darin, dass eine Funktion von der JavaScript-Engine erstellt wird.
Ein Funktionsausdruck wird erstellt, wenn die Ausführung ihn erreicht, und ist erst ab diesem Zeitpunkt verwendbar.
Sobald der Ausführungsfluss die rechte Seite der Zuweisung let sum = function…
erreicht, ist die Funktion erstellt und kann von nun an verwendet werden (zugewiesen, aufgerufen, etc.).
Funktionsdeklarationen sind anders.
Eine Funktionsdeklaration kann früher aufgerufen werden, als sie definiert ist.
Eine globale Funktionsdeklaration ist zum Beispiel im ganzen Skript sichtbar, egal wo sie sich befindet.
Das liegt an internen Algorithmen. Wenn JavaScript die Ausführung des Skripts vorbereitet, sucht es zuerst nach globalen Funktionsdeklarationen darin und erstellt die Funktionen. Man kann sich das als eine „Initialisierungsphase“ vorstellen.
Und nachdem alle Funktionsdeklarationen verarbeitet wurden, wird der Code ausgeführt. Er hat also Zugriff auf diese Funktionen.
Das funktioniert zum Beispiel so:
sayHi("John"); // Hello, Johnfunction sayHi(name) { alert( `Hello, ${name}` );}
Die Funktionsdeklaration sayHi
wird erstellt, wenn JavaScript den Start des Skripts vorbereitet und ist überall im Skript sichtbar.
…Wäre es ein Funktionsausdruck, dann würde es nicht funktionieren:
sayHi("John"); // error!let sayHi = function(name) { // (*) no magic any more alert( `Hello, ${name}` );};
Funktionsausdrücke werden erstellt, wenn die Ausführung sie erreicht. Das würde nur in der Zeile (*)
passieren. Zu spät.
Eine weitere Besonderheit von Funktionsdeklarationen ist ihr Blockumfang.
Im Strict-Modus ist eine Funktionsdeklaration, die sich innerhalb eines Codeblocks befindet, überall innerhalb dieses Blocks sichtbar. Aber nicht außerhalb des Blocks.
Angenommen, wir müssen eine Funktion welcome()
deklarieren, die von der Variable age
abhängt, die wir während der Laufzeit erhalten. Wenn wir die Funktionsdeklaration verwenden, wird sie nicht wie vorgesehen funktionieren:
let age = prompt("What is your age?", 18);// conditionally declare a functionif (age < 18) { function welcome() { alert("Hello!"); }} else { function welcome() { alert("Greetings!"); }}// ...use it laterwelcome(); // Error: welcome is not defined
Das liegt daran, dass eine Funktionsdeklaration nur innerhalb des Codeblocks sichtbar ist, in dem sie sich befindet.
Hier ein weiteres Beispiel:
let age = 16; // take 16 as an exampleif (age < 18) { welcome(); // \ (runs) // | function welcome() { // | alert("Hello!"); // | Function Declaration is available } // | everywhere in the block where it's declared // | welcome(); // / (runs)} else { function welcome() { alert("Greetings!"); }}// Here we're out of curly braces,// so we can not see Function Declarations made inside of them.welcome(); // Error: welcome is not defined
Was können wir tun, um welcome
außerhalb von if
sichtbar zu machen?
Der richtige Ansatz wäre, einen Funktionsausdruck zu verwenden und welcome
der Variablen zuzuweisen, die außerhalb von if
deklariert ist und die richtige Sichtbarkeit hat.
Dieser Code funktioniert wie vorgesehen:
let age = prompt("What is your age?", 18);let welcome;if (age < 18) { welcome = function() { alert("Hello!"); };} else { welcome = function() { alert("Greetings!"); };}welcome(); // ok now
Oder wir könnten ihn noch weiter vereinfachen, indem wir einen Fragezeichenoperator ?
verwenden:
let age = prompt("What is your age?", 18);let welcome = (age < 18) ? function() { alert("Hello!"); } : function() { alert("Greetings!"); };welcome(); // ok now
Als Faustregel gilt: Wenn wir eine Funktion deklarieren müssen, sollten wir zuerst die Syntax der Funktionsdeklaration wählen. Sie gibt uns mehr Freiheit bei der Organisation unseres Codes, da wir solche Funktionen aufrufen können, bevor sie deklariert werden.
Das ist auch besser für die Lesbarkeit, da es einfacher ist, function f(…) {…}
im Code nachzuschlagen als let f = function(…) {…};
. Funktionsdeklarationen sind „auffälliger“.
…Aber wenn eine Funktionsdeklaration aus irgendeinem Grund nicht passt oder wir eine bedingte Deklaration brauchen (wir haben gerade ein Beispiel gesehen), dann sollte ein Funktionsausdruck verwendet werden.
Zusammenfassung
- Funktionen sind Werte. Sie können an jeder beliebigen Stelle des Codes zugewiesen, kopiert oder deklariert werden.
- Wenn die Funktion als separate Anweisung im Hauptcodefluss deklariert wird, nennt man das eine „Funktionsdeklaration“.
- Wenn die Funktion als Teil eines Ausdrucks erstellt wird, nennt man das einen „Funktionsausdruck“.
- Funktionsdeklarationen werden verarbeitet, bevor der Codeblock ausgeführt wird. Sie sind überall im Block sichtbar.
- Funktionsausdrücke werden erstellt, wenn der Ausführungsfluss sie erreicht.
In den meisten Fällen, in denen wir eine Funktion deklarieren müssen, ist eine Funktionsdeklaration vorzuziehen, da sie vor der eigentlichen Deklaration sichtbar ist. Das gibt uns mehr Flexibilität bei der Codeorganisation und ist in der Regel besser lesbar.
Wir sollten also einen Funktionsausdruck nur dann verwenden, wenn eine Funktionsdeklaration für die Aufgabe nicht geeignet ist. Wir haben in diesem Kapitel ein paar Beispiele dafür gesehen und werden in Zukunft noch mehr sehen.