Sehr oft müssen wir an vielen Stellen des Skripts eine ähnliche Aktion ausführen.
Beispielsweise müssen wir eine ansprechende Meldung anzeigen, wenn sich ein Besucher anmeldet, abmeldet und vielleicht woanders.
Funktionen sind die wichtigsten „Bausteine“ des Programms. Sie ermöglichen den mehrfachen Aufruf des Codes ohne Wiederholung.
Wir haben bereits Beispiele für integrierte Funktionen gesehen, wie zum Beispiel alert(message)
, prompt(message, default)
und confirm(question)
. Wir können aber auch eigene Funktionen erstellen.
Um eine Funktion zu erstellen, können wir eine Funktionsdeklaration verwenden.
Es sieht so aus:
Funktion showMessage() { alarm( 'Hallo zusammen!' ); }
Zuerst steht das Schlüsselwort function
, dann der Name der Funktion , dann eine Liste von Parametern zwischen den Klammern (durch Kommas getrennt, leer im Beispiel oben, Beispiele sehen wir später) und schließlich der Code der Funktion, ebenfalls benannt „der Funktionskörper“, zwischen geschweiften Klammern.
Funktionsname(Parameter1, Parameter2, ... ParameterN) { // Körper }
Unsere neue Funktion kann mit ihrem Namen aufgerufen werden: showMessage()
.
Zum Beispiel:
Funktion showMessage() { alarm( 'Hallo zusammen!' ); } showMessage(); showMessage();
Der Aufruf showMessage()
führt den Code der Funktion aus. Hier sehen wir die Nachricht zweimal.
Dieses Beispiel zeigt deutlich einen der Hauptzwecke von Funktionen: die Vermeidung von Codeduplizierungen.
Wenn wir die Nachricht oder die Art und Weise, wie sie angezeigt wird, jemals ändern müssen, reicht es aus, den Code an einer Stelle zu ändern: der Funktion, die sie ausgibt.
Eine innerhalb einer Funktion deklarierte Variable ist nur innerhalb dieser Funktion sichtbar.
Zum Beispiel:
Funktion showMessage() { let message = „Hallo, ich bin JavaScript!“; // lokale Variable Warnung( Nachricht); } showMessage(); // Hallo, ich bin JavaScript! Warnung( Nachricht); // <-- Fehler! Die Variable ist lokal für die Funktion
Eine Funktion kann auch auf eine äußere Variable zugreifen, zum Beispiel:
let userName = 'John'; Funktion showMessage() { let message = 'Hallo, ' + Benutzername; Warnung (Nachricht); } showMessage(); // Hallo, John
Die Funktion hat vollen Zugriff auf die äußere Variable. Es kann es auch ändern.
Zum Beispiel:
let userName = 'John'; Funktion showMessage() { Benutzername = „Bob“; // (1) hat die äußere Variable geändert let message = 'Hallo, ' + Benutzername; Warnung (Nachricht); } alarm( Benutzername ); // John vor dem Funktionsaufruf showMessage(); alarm( Benutzername ); // Bob, der Wert wurde von der Funktion geändert
Die äußere Variable wird nur verwendet, wenn keine lokale Variable vorhanden ist.
Wenn eine gleichnamige Variable innerhalb der Funktion deklariert wird, überschattet sie die äußere Variable. Im folgenden Code verwendet die Funktion beispielsweise den lokalen userName
. Der äußere wird ignoriert:
let userName = 'John'; Funktion showMessage() { let userName = „Bob“; // eine lokale Variable deklarieren let message = 'Hallo, ' + Benutzername; // Bob Warnung (Nachricht); } // Die Funktion erstellt und verwendet ihren eigenen Benutzernamen showMessage(); alarm( Benutzername ); // John, unverändert, die Funktion hat nicht auf die äußere Variable zugegriffen
Globale Variablen
Variablen, die außerhalb einer Funktion deklariert werden, wie z. B. der äußere userName
im obigen Code, werden als global bezeichnet.
Globale Variablen sind von jeder Funktion aus sichtbar (es sei denn, sie werden von lokalen Variablen überschattet).
Es empfiehlt sich, die Verwendung globaler Variablen zu minimieren. Moderner Code hat wenige oder keine Globals. Die meisten Variablen liegen in ihren Funktionen. Manchmal können sie jedoch nützlich sein, um Daten auf Projektebene zu speichern.
Mithilfe von Parametern können wir beliebige Daten an Funktionen übergeben.
Im folgenden Beispiel hat die Funktion zwei Parameter: from
und text
.
Funktion showMessage(from, text) { // Parameter: from, text alarm(from + ': ' + text); } showMessage('Ann', 'Hallo!'); // Ann: Hallo! (*) showMessage('Ann', "Was ist los?"); // Ann: Was ist los? (**)
Beim Aufruf der Funktion in den Zeilen (*)
und (**)
werden die angegebenen Werte in lokale Variablen from
und text
kopiert. Dann verwendet die Funktion sie.
Hier ist ein weiteres Beispiel: Wir haben eine Variable from
und übergeben sie an die Funktion. Bitte beachten Sie: Die Funktion ändert sich from
, aber die Änderung ist außerhalb nicht sichtbar, da eine Funktion immer eine Kopie des Werts erhält:
Funktion showMessage(from, text) { von = '*' + von + '*'; // „von“ schöner aussehen lassen alarm( from + ': ' + text ); } let from = „Ann“; showMessage(from, „Hallo“); // *Ann*: Hallo // Der Wert von „from“ ist derselbe, die Funktion hat eine lokale Kopie geändert alarm( von ); // Ann
Wenn ein Wert als Funktionsparameter übergeben wird, wird er auch als Argument bezeichnet.
Mit anderen Worten, um diese Begriffe klarzustellen:
Ein Parameter ist die Variable, die in der Funktionsdeklaration in Klammern aufgeführt ist (es handelt sich um einen Deklarationszeitbegriff).
Ein Argument ist der Wert, der an die Funktion übergeben wird, wenn sie aufgerufen wird (es ist ein Aufrufzeitbegriff).
Wir deklarieren Funktionen mit einer Auflistung ihrer Parameter und rufen sie dann mit Argumenten auf.
Im obigen Beispiel könnte man sagen: „Die Funktion showMessage
wird mit zwei Parametern deklariert und dann mit zwei Argumenten aufgerufen: from
und "Hello"
“.
Wenn eine Funktion aufgerufen wird, aber kein Argument angegeben wird, wird der entsprechende Wert undefined
.
Beispielsweise kann die oben erwähnte Funktion showMessage(from, text)
mit einem einzigen Argument aufgerufen werden:
showMessage("Ann");
Das ist kein Fehler. Ein solcher Aufruf würde "*Ann*: undefined"
ausgeben. Da der Wert für text
nicht übergeben wird, wird er undefined
.
Wir können den sogenannten „Standard“-Wert (zu verwenden, wenn er weggelassen wird) für einen Parameter in der Funktionsdeklaration angeben, indem wir =
verwenden:
Funktion showMessage(from, text = "kein Text angegeben") { alarm( from + ": " + text ); } showMessage("Ann"); // Ann: kein Text angegeben
Wenn nun der text
nicht übergeben wird, erhält er den Wert "no text given"
.
Der Standardwert springt auch ein, wenn der Parameter vorhanden ist, aber strikt gleich undefined
ist, wie folgt:
showMessage("Ann", undefiniert); // Ann: kein Text angegeben
Hier ist "no text given"
ein String, es kann aber auch ein komplexerer Ausdruck sein, der nur ausgewertet und zugewiesen wird, wenn der Parameter fehlt. Das ist also auch möglich:
Funktion showMessage(from, text = anotherFunction()) { // anotherFunction() wird nur ausgeführt, wenn kein Text angegeben ist // sein Ergebnis wird zum Wert von Text }
Auswertung von Standardparametern
In JavaScript wird bei jedem Aufruf der Funktion ohne den entsprechenden Parameter ein Standardparameter ausgewertet.
Im obigen Beispiel wird anotherFunction()
überhaupt nicht aufgerufen, wenn der text
bereitgestellt wird.
Andererseits wird es jedes Mal unabhängig aufgerufen, wenn text
fehlt.
Standardparameter im alten JavaScript-Code
Vor einigen Jahren unterstützte JavaScript die Syntax für Standardparameter nicht. Deshalb verwendeten die Leute andere Möglichkeiten, sie zu spezifizieren.
Heutzutage können wir sie in alten Drehbüchern finden.
Beispielsweise eine explizite Prüfung auf undefined
:
Funktion showMessage(from, text) { if (text === undefiniert) { text = 'kein Text angegeben'; } alarm( from + ": " + text ); }
…Oder mit ||
Operator:
Funktion showMessage(from, text) { // Wenn der Textwert falsch ist, weisen Sie den Standardwert zu // Dies setzt voraus, dass text == "" dasselbe ist wie überhaupt kein Text Text = Text || 'kein Text angegeben'; ... }
Manchmal ist es sinnvoll, Parametern zu einem späteren Zeitpunkt nach der Funktionsdeklaration Standardwerte zuzuweisen.
Wir können überprüfen, ob der Parameter während der Funktionsausführung übergeben wird, indem wir ihn mit undefined
vergleichen:
Funktion showMessage(text) { // ... if (text === undefiniert) { // wenn der Parameter fehlt text = 'leere Nachricht'; } Warnung(Text); } showMessage(); // leere Nachricht
…Oder wir könnten das ||
verwenden Operator:
Funktion showMessage(text) { // wenn der Text undefiniert oder anderweitig falsch ist, setze ihn auf „leer“ Text = Text || 'leer'; ... }
Moderne JavaScript-Engines unterstützen den Nullish-Coalescing-Operator ??
, ist es besser, wenn die meisten falschen Werte, wie z. B. 0
, als „normal“ betrachtet werden sollten:
Funktion showCount(count) { // wenn count undefiniert oder null ist, zeige „unknown“ alarm(count ?? "unknown"); } showCount(0); // 0 showCount(null); // unbekannt showCount(); // unbekannt
Eine Funktion kann als Ergebnis einen Wert an den aufrufenden Code zurückgeben.
Das einfachste Beispiel wäre eine Funktion, die zwei Werte summiert:
Funktion sum(a, b) { gib a + b zurück; } let result = sum(1, 2); Warnung( Ergebnis); // 3
Die Direktive return
kann an jeder Stelle der Funktion stehen. Wenn die Ausführung diesen erreicht, stoppt die Funktion und der Wert wird an den aufrufenden Code zurückgegeben (oben dem result
zugewiesen).
In einer einzelnen Funktion kann es viele Vorkommen von return
geben. Zum Beispiel:
Funktion checkAge(age) { if (Alter >= 18) { return true; } anders { return bestätigen('Haben Sie die Erlaubnis Ihrer Eltern?'); } } let age = prompt('Wie alt bist du?', 18); if ( checkAge(age) ) { alarm( 'Zugriff gewährt' ); } anders { alarm( 'Zugriff verweigert' ); }
Es ist möglich, return
ohne Wert zu verwenden. Dadurch wird die Funktion sofort beendet.
Zum Beispiel:
Funktion showMovie(age) { if ( !checkAge(age) ) { zurückkehren; } Alert( "Zeige dir den Film" ); // (*) // ... }
Wenn checkAge(age)
im obigen Code false
zurückgibt, fährt showMovie
nicht mit der alert
fort.
Eine Funktion mit oder ohne leere return
gibt undefined
zurück
Wenn eine Funktion keinen Wert zurückgibt, ist das dasselbe, als ob sie undefined
zurückgibt:
function doNothing() { /* empty */ } alarm( doNothing() === undefiniert ); // WAHR
Eine leere return
ist auch dasselbe wie return undefined
:
Funktion doNothing() { zurückkehren; } alarm( doNothing() === undefiniert ); // WAHR
Fügen Sie niemals eine neue Zeile zwischen return
und Wert ein
Für einen langen Ausdruck in return
könnte es verlockend sein, ihn in eine separate Zeile zu schreiben, etwa so:
zurückkehren (einige + lange + Ausdruck + oder + was auch immer * f(a) + f(b))
Das funktioniert nicht, da JavaScript nach return
ein Semikolon voraussetzt. Das funktioniert genauso wie:
zurückkehren; (einige + lange + Ausdruck + oder + was auch immer * f(a) + f(b))
Es handelt sich also praktisch um eine leere Rückgabe.
Wenn wir möchten, dass der zurückgegebene Ausdruck über mehrere Zeilen umgebrochen wird, sollten wir ihn in derselben Zeile wie return
beginnen. Oder setzen Sie dort zumindest die öffnenden Klammern wie folgt ein:
zurückkehren ( einige + lange + Ausdruck + oder + was auch immer * f(a) + f(b) )
Und es wird genauso funktionieren, wie wir es erwarten.
Funktionen sind Aktionen. Daher ist ihr Name normalerweise ein Verb. Es sollte kurz und so genau wie möglich sein und beschreiben, was die Funktion tut, damit jemand, der den Code liest, einen Hinweis darauf erhält, was die Funktion tut.
Es ist eine weit verbreitete Praxis, eine Funktion mit einem verbalen Präfix zu beginnen, das die Aktion vage beschreibt. Über die Bedeutung der Präfixe muss im Team Einigkeit bestehen.
Beispielsweise zeigen Funktionen, die mit "show"
beginnen, normalerweise etwas an.
Funktion beginnend mit…
"get…"
– einen Wert zurückgeben,
"calc…"
– etwas berechnen,
"create…"
– etwas erschaffen,
"check…"
– etwas überprüfen und einen booleschen Wert zurückgeben usw.
Beispiele für solche Namen:
showMessage(..) // zeigt eine Nachricht an getAge(..) // gibt das Alter zurück (bekommt es irgendwie) calcSum(..) // berechnet eine Summe und gibt das Ergebnis zurück createForm(..) // erstellt ein Formular (und gibt es normalerweise zurück) checkPermission(..) // prüft eine Berechtigung und gibt true/false zurück
Wenn Präfixe vorhanden sind, können Sie anhand eines Blicks auf einen Funktionsnamen erkennen, welche Art von Arbeit diese leistet und welchen Wert sie zurückgibt.
Eine Funktion – eine Aktion
Eine Funktion sollte genau das tun, was ihr Name suggeriert, nicht mehr.
Zwei unabhängige Aktionen verdienen normalerweise zwei Funktionen, auch wenn sie normalerweise zusammen aufgerufen werden (in diesem Fall können wir eine dritte Funktion erstellen, die diese beiden aufruft).
Einige Beispiele für einen Verstoß gegen diese Regel:
getAge
– wäre schlecht, wenn eine alert
mit dem Alter angezeigt würde (sollte nur get sein).
createForm
– wäre schlecht, wenn es das Dokument ändert und ihm ein Formular hinzufügt (sollte es nur erstellen und zurückgeben).
checkPermission
– wäre schlecht, wenn die Meldung access granted/denied
angezeigt würde (sollte nur die Prüfung durchführen und das Ergebnis zurückgeben).
Diese Beispiele gehen von der gemeinsamen Bedeutung von Präfixen aus. Es steht Ihnen und Ihrem Team frei, sich auf andere Bedeutungen zu einigen, aber normalerweise unterscheiden sie sich nicht wesentlich. Auf jeden Fall sollten Sie genau wissen, was ein Präfix bedeutet und was eine Funktion mit Präfix leisten kann und was nicht. Alle Funktionen mit demselben Präfix sollten den Regeln entsprechen. Und das Team sollte das Wissen teilen.
Ultrakurze Funktionsnamen
Funktionen, die sehr häufig verwendet werden, haben manchmal ultrakurze Namen.
Beispielsweise definiert das jQuery-Framework eine Funktion mit $
. Die Kernfunktion der Lodash-Bibliothek heißt _
.
Dies sind Ausnahmen. Im Allgemeinen sollten Funktionsnamen prägnant und beschreibend sein.
Funktionen sollten kurz sein und genau eine Sache tun. Wenn das Ding groß ist, lohnt es sich vielleicht, die Funktion in ein paar kleinere Funktionen aufzuteilen. Manchmal ist es vielleicht nicht so einfach, diese Regel zu befolgen, aber es ist auf jeden Fall eine gute Sache.
Eine separate Funktion ist nicht nur einfacher zu testen und zu debuggen – ihre bloße Existenz ist ein großartiger Kommentar!
Vergleichen Sie beispielsweise die beiden folgenden Funktionen showPrimes(n)
. Jeder gibt Primzahlen bis n
aus.
Die erste Variante verwendet ein Label:
Funktion showPrimes(n) { nextPrime: for (let i = 2; i < n; i++) { for (let j = 2; j < i; j++) { if (i % j == 0) continue nextPrime; } alarm( i ); // eine Primzahl } }
Die zweite Variante verwendet eine zusätzliche Funktion isPrime(n)
um die Primalität zu testen:
Funktion showPrimes(n) { for (sei i = 2; i < n; i++) { if (!isPrime(i)) continue; alarm(i); // eine Primzahl } } Funktion isPrime(n) { for (sei i = 2; i < n; i++) { if ( n % i == 0) return false; } return true; }
Die zweite Variante ist doch einfacher zu verstehen, oder? Anstelle des Codeteils sehen wir einen Namen der Aktion ( isPrime
). Manchmal bezeichnen Leute solchen Code als selbstbeschreibend .
Daher können Funktionen erstellt werden, auch wenn wir nicht beabsichtigen, sie wiederzuverwenden. Sie strukturieren den Code und machen ihn lesbar.
Eine Funktionsdeklaration sieht so aus:
Funktionsname(Parameter, getrennt, durch, Komma) { /* Code */ }
Als Parameter an eine Funktion übergebene Werte werden in ihre lokalen Variablen kopiert.
Eine Funktion kann auf äußere Variablen zugreifen. Aber es funktioniert nur von innen nach außen. Der Code außerhalb der Funktion sieht seine lokalen Variablen nicht.
Eine Funktion kann einen Wert zurückgeben. Ist dies nicht der Fall, ist das Ergebnis undefined
.
Um den Code übersichtlich und leicht verständlich zu gestalten, wird empfohlen, in der Funktion hauptsächlich lokale Variablen und Parameter und keine äußeren Variablen zu verwenden.
Es ist immer einfacher, eine Funktion zu verstehen, die Parameter abruft, mit ihnen arbeitet und ein Ergebnis zurückgibt, als eine Funktion, die keine Parameter abruft, aber als Nebeneffekt äußere Variablen ändert.
Funktionsbenennung:
Ein Name sollte klar beschreiben, was die Funktion tut. Wenn wir im Code einen Funktionsaufruf sehen, können wir anhand eines guten Namens sofort verstehen, was er bewirkt und zurückgibt.
Eine Funktion ist eine Aktion, daher sind Funktionsnamen normalerweise verbal.
Es gibt viele bekannte Funktionspräfixe wie create…
, show…
, get…
, check…
und so weiter. Verwenden Sie sie, um anzudeuten, was eine Funktion tut.
Funktionen sind die Hauptbausteine von Skripten. Jetzt haben wir die Grundlagen behandelt, sodass wir tatsächlich mit der Erstellung und Verwendung beginnen können. Aber das ist nur der Anfang des Weges. Wir werden noch oft darauf zurückkommen und uns eingehender mit ihren erweiterten Funktionen befassen.
Wichtigkeit: 4
Die folgende Funktion gibt true
zurück, wenn der Parameter „ age
“ größer als 18
ist.
Andernfalls fragt es nach einer Bestätigung und gibt sein Ergebnis zurück:
Funktion checkAge(age) { wenn (Alter > 18) { return true; } anders { // ... return bestätigen('Haben deine Eltern es dir erlaubt?'); } }
Funktioniert die Funktion anders, wenn else
entfernt wird?
Funktion checkAge(age) { wenn (Alter > 18) { return true; } // ... return bestätigen('Haben deine Eltern es dir erlaubt?'); }
Gibt es einen Unterschied im Verhalten dieser beiden Varianten?
Kein Unterschied!
In beiden Fällen return confirm('Did parents allow you?')
genau dann ausgeführt, wenn die if
-Bedingung falsch ist.
Wichtigkeit: 4
Die folgende Funktion gibt true
zurück, wenn der Parameter „ age
“ größer als 18
ist.
Andernfalls fragt es nach einer Bestätigung und gibt sein Ergebnis zurück.
Funktion checkAge(age) { wenn (Alter > 18) { return true; } anders { return bestätigen('Haben deine Eltern es dir erlaubt?'); } }
Schreiben Sie es neu, um dasselbe, jedoch ohne if
, in einer einzigen Zeile auszuführen.
Erstellen Sie zwei Varianten von checkAge
:
Verwenden Sie einen Fragezeichenoperator ?
Mit OR ||
Die Verwendung eines Fragezeichenoperators '?'
:
Funktion checkAge(age) { Rückkehr (Alter > 18)? true : bestätigen('Haben deine Eltern es dir erlaubt?'); }
Mit OR ||
(die kürzeste Variante):
Funktion checkAge(age) { Rückkehr (Alter > 18) || bestätigen('Haben deine Eltern es dir erlaubt?'); }
Beachten Sie, dass die Klammern um age > 18
hier nicht erforderlich sind. Sie dienen der besseren Lesbarkeit.
Wichtigkeit: 1
Schreiben Sie eine Funktion min(a,b)
die die kleinste der beiden Zahlen a
und b
zurückgibt.
Zum Beispiel:
min(2, 5) == 2 min(3, -1) == -1 min(1, 1) == 1
Eine Lösung mit if
:
Funktion min(a, b) { wenn (a < b) { gib a zurück; } anders { Rückkehr b; } }
Eine Lösung mit einem Fragezeichenoperator '?'
:
Funktion min(a, b) { a < b zurückgeben? a : b; }
PS Im Falle einer Gleichheit a == b
spielt es keine Rolle, was zurückgegeben werden soll.
Wichtigkeit: 4
Schreiben Sie eine Funktion pow(x,n)
die x
in Potenz n
zurückgibt. Oder anders ausgedrückt: Multipliziert x
n
-mal mit sich selbst und gibt das Ergebnis zurück.
pow(3, 2) = 3 * 3 = 9 pow(3, 3) = 3 * 3 * 3 = 27 pow(1, 100) = 1 * 1 * ...* 1 = 1
Erstellen Sie eine Webseite, die zur Eingabe von x
und n
auffordert und dann das Ergebnis von pow(x,n)
anzeigt.
Führen Sie die Demo aus
PS: In dieser Aufgabe sollte die Funktion nur natürliche Werte von n
unterstützen: Ganzzahlen ab 1
.
Funktion pow(x, n) { sei Ergebnis = x; for (sei i = 1; i < n; i++) { Ergebnis *= x; } Rückgabeergebnis; } let x = prompt("x?", ''); let n = prompt("n?", ''); wenn (n < 1) { Alert(`Power ${n} wird nicht unterstützt, verwenden Sie eine positive Ganzzahl`); } anders { alarm( pow(x, n) ); }