Funktionen — wiederverwendbare Codeblöcke
Ein weiteres essentielles Konzept beim Programmieren sind Funktionen. Funktionen erlauben es Ihnen, einen Codeabschnitt, der eine einzelne Aufgabe erfüllt, in einem definierten Block zu speichern und dann diesen Code bei Bedarf mit einem einzigen kurzen Befehl aufzurufen – anstatt denselben Code immer wieder schreiben zu müssen. In diesem Artikel werden wir grundlegende Konzepte hinter Funktionen erforschen, wie die grundlegende Syntax, wie man sie aufruft und definiert, Scope (Gültigkeitsbereich) und Parameter.
Voraussetzungen: | Ein Verständnis von HTML und den Grundlagen von CSS, sowie Vertrautheit mit den JavaScript-Grundlagen, wie in früheren Lektionen behandelt. |
---|---|
Lernziele: |
|
Wo finde ich Funktionen?
In JavaScript finden Sie Funktionen überall. Tatsächlich haben wir bereits im gesamten Kurs Funktionen genutzt; wir haben nur noch nicht viel darüber gesprochen. Jetzt ist die Zeit gekommen, dass wir beginnen, explizit über Funktionen zu sprechen und ihre Syntax wirklich zu erkunden.
Wann immer Sie eine JavaScript-Struktur mit einem Paar von Klammern – ()
– verwenden und Sie nicht eine übliche eingebaute Sprachstruktur wie eine for-Schleife, while- oder do...while-Schleife oder ein if...else Statement verwenden, machen Sie Gebrauch von einer Funktion.
Eingebaute Browser-Funktionen
Wir haben in diesem Kurs oft Funktionen verwendet, die in den Browser integriert sind.
Jedes Mal, wenn wir beispielsweise einen Textstring manipulierten:
const myText = "I am a string";
const newString = myText.replace("string", "sausage");
console.log(newString);
// the replace() string function takes a source string,
// and a target string and replaces the source string,
// with the target string, and returns the newly formed string
Oder jedes Mal, wenn wir ein Array manipulierten:
const myArray = ["I", "love", "chocolate", "frogs"];
const madeAString = myArray.join(" ");
console.log(madeAString);
// the join() function takes an array, joins
// all the array items together into a single
// string, and returns this new string
Oder wenn wir eine Zufallszahl generierten:
const myNumber = Math.random();
// the random() function generates a random number between
// 0 and up to but not including 1, and returns that number
Haben wir eine Funktion verwendet!
Hinweis: Sie können diese Zeilen gerne in die JavaScript-Konsole Ihres Browsers eingeben, um sich bei Bedarf mit deren Funktionalität vertraut zu machen.
Die JavaScript-Sprache hat viele eingebaute Funktionen, die es Ihnen ermöglichen, nützliche Dinge zu tun, ohne dass Sie all diesen Code selbst schreiben müssen. Tatsächlich konnte nicht einmal ein Teil des Codes, den Sie beim Aufrufen (ein vornehmes Wort für Ausführen) einer eingebauten Browser-Funktion aufrufen, in JavaScript geschrieben werden — viele dieser Funktionen rufen Teile des Background-Browsercodes auf, der hauptsächlich in Low-Level-Systemsprachen wie C++ geschrieben ist, nicht in Websprachen wie JavaScript.
Bedenken Sie, dass einige eingebaute Browser-Funktionen nicht Teil der JavaScript-Kernsprache sind — einige sind als Teil von Browser-APIs definiert, die auf der Standardsprache aufbauen, um noch mehr Funktionalität bereitzustellen (siehe diesen frühen Abschnitt unseres Kurses für weitere Beschreibungen). Wir werden später in einem Modul detaillierter auf die Verwendung von Browser-APIs eingehen.
Funktionen versus Methoden
Teil von Objekten sind Methoden; Sie werden später im Modul mehr über Objekte erfahren. Im Moment wollten wir nur mögliche Verwirrung zwischen Methode und Funktion klären – Sie werden wahrscheinlich auf beide Begriffe stoßen, wenn Sie sich die verfügbaren verwandten Ressourcen im Web ansehen.
Der Code, den wir bisher verwendet haben, gibt es in beiden Formen: Funktionen und Methoden. Die vollständige Liste der eingebauten Funktionen sowie die eingebauten Objekte und ihre entsprechenden Methoden können Sie hier einsehen.
Sie haben auch viele benutzerdefinierte Funktionen im Kurs gesehen — Funktionen, die in Ihrem Code definiert sind, nicht im Browser. Jedes Mal, wenn Sie einen benutzerdefinierten Namen mit Klammern unmittelbar danach gesehen haben, haben Sie eine benutzerdefinierte Funktion verwendet. In unserem Beispiel random-canvas-circles.html (sehen Sie auch den vollständigen Quellcode) aus unserem Schleifenartikel, haben wir eine benutzerdefinierte draw()
Funktion eingebaut, die so aussah:
function draw() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
for (let i = 0; i < 100; i++) {
ctx.beginPath();
ctx.fillStyle = "rgb(255 0 0 / 50%)";
ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
ctx.fill();
}
}
Diese Funktion zeichnet 100 zufällige Kreise in ein <canvas>
Element. Wann immer wir das tun möchten, können wir einfach die Funktion mit diesem Aufruf:
draw();
anstatt all diesen Code jedes Mal ausschreiben zu müssen, wenn wir es wiederholen wollen. Funktionen können jeden gewünschten Code enthalten — Sie können sogar andere Funktionen innerhalb von Funktionen aufrufen. Die obige Funktion ruft beispielsweise die random()
Funktion dreimal auf, die durch den folgenden Code definiert ist:
function random(number) {
return Math.floor(Math.random() * number);
}
Wir benötigten diese Funktion, weil die eingebaute Browserfunktion Math.random()
nur eine zufällige Dezimalzahl zwischen 0 und 1 generiert. Wir wollten jedoch eine zufällige Ganzzahl zwischen 0 und einer angegebenen Zahl.
Das Aufrufen von Funktionen
Sie sind sich dessen wahrscheinlich bereits bewusst, aber nur für den Fall: Um tatsächlich eine Funktion se nach ihrer Definition zu verwenden, müssen Sie diese ausführen – oder aufrufen. Dies geschieht, indem Sie den Namen der Funktion irgendwo im Code, gefolgt von Klammern, einfügen.
function myFunction() {
alert("hello");
}
myFunction();
// calls the function once
Hinweis: Diese Art der Erstellung einer Funktion wird auch als Funktionsdeklaration bezeichnet. Sie wird immer hochgehoben, sodass Sie die Funktion über der Funktionsdefinition aufrufen können und es wird trotzdem funktionieren.
Funktionsparameter
Für das Aufrufen erfordern einige Funktionen die Angabe von Parametern — dies sind Werte, die in den Funktionsklammern enthalten sein müssen, damit sie ihre Aufgabe richtig erfüllen können.
Hinweis: Parameter werden manchmal auch Argumente, Eigenschaften oder sogar Attribute genannt.
Zum Beispiel benötigt die eingebaute Browserfunktion Math.random()
keine Parameter. Wenn sie aufgerufen wird, gibt sie immer eine Zufallszahl zwischen 0 und 1 zurück:
const myNumber = Math.random();
Die eingebaute String-Funktion replace()
des Browsers benötigt jedoch zwei Parameter – den zu findenden Unterstring im Hauptstring und den zu ersetzenden Unterstring:
const myText = "I am a string";
const newString = myText.replace("string", "sausage");
Hinweis: Wenn Sie mehrere Parameter angeben müssen, werden sie durch Kommas getrennt.
Optionale Parameter
Manchmal sind Parameter optional — Sie müssen sie nicht angeben. Wenn Sie es nicht tun, übernimmt die Funktion im Allgemeinen ein Standardverhalten. Zum Beispiel ist der Parameter der Array-Funktion join()
optional:
const myArray = ["I", "love", "chocolate", "frogs"];
const madeAString = myArray.join(" ");
console.log(madeAString);
// returns 'I love chocolate frogs'
const madeAnotherString = myArray.join();
console.log(madeAnotherString);
// returns 'I,love,chocolate,frogs'
Wenn kein Parameter eingeschlossen wird, um ein Verbindungs-/Trennzeichen zu spezifizieren, wird standardmäßig ein Komma verwendet.
Standardparameter
Wenn Sie eine Funktion schreiben und optionale Parameter unterstützen möchten, können Sie Standardwerte angeben, indem Sie =
nach dem Namen des Parameters hinzufügen, gefolgt vom Standardwert:
function hello(name = "Chris") {
console.log(`Hello ${name}!`);
}
hello("Ari"); // Hello Ari!
hello(); // Hello Chris!
Anonyme Funktionen und Pfeilfunktionen
Bisher haben wir eine Funktion so erstellt:
function myFunction() {
alert("hello");
}
Aber Sie können auch eine Funktion erstellen, die keinen Namen hat:
(function () {
alert("hello");
});
Dies wird als anonyme Funktion bezeichnet, da sie keinen Namen hat. Sie sehen oft anonyme Funktionen, wenn eine Funktion erwartet, dass eine andere Funktion als Parameter übergeben wird. In diesem Fall wird der Funktionsparameter oft als anonyme Funktion übergeben.
Hinweis: Diese Art der Erstellung einer Funktion wird auch als Funktionsausdruck bezeichnet. Im Gegensatz zu Funktionsdeklarationen werden Funktionsausdrücke nicht hochgehoben.
Beispiel einer anonymen Funktion
Zum Beispiel, sagen wir, Sie möchten einen Code ausführen, wenn der Benutzer in ein Textfeld tippt. Dafür können Sie die Funktion addEventListener()
des Textfelds aufrufen. Diese Funktion erwartet, dass Sie ihr (mindestens) zwei Parameter übergeben:
- den Namen des Ereignisses, das abgehört werden soll, in diesem Fall
keydown
- eine Funktion, die ausgeführt wird, wenn das Ereignis eintritt.
Wenn der Benutzer eine Taste drückt, ruft der Browser die von Ihnen bereitgestellte Funktion auf und gibt ihr einen Parameter mit Informationen zu diesem Ereignis, einschließlich der besonderen Taste, die der Benutzer gedrückt hat:
function logKey(event) {
console.log(`You pressed "${event.key}".`);
}
textBox.addEventListener("keydown", logKey);
Anstatt eine separate logKey()
Funktion zu definieren, können Sie addEventListener()
eine anonyme Funktion übergeben:
textBox.addEventListener("keydown", function (event) {
console.log(`You pressed "${event.key}".`);
});
Pfeilfunktionen
Wenn Sie eine anonyme Funktion auf diese Weise übergeben, gibt es eine alternative Form, die Sie verwenden können, die sogenannte Pfeilfunktion. Anstelle von function(event)
, schreiben Sie (event) =>
:
textBox.addEventListener("keydown", (event) => {
console.log(`You pressed "${event.key}".`);
});
Wenn die Funktion nur einen Parameter benötigt, können Sie die Klammern um den Parameter weglassen:
textBox.addEventListener("keydown", event => {
console.log(`You pressed "${event.key}".`);
});
Schließlich, wenn Ihre Funktion nur eine Zeile enthält, die eine return
-Anweisung darstellt, können Sie auch die geschweiften Klammern und das return
-Schlüsselwort weglassen und den Ausdruck implizit zurückgeben. Im folgenden Beispiel verwenden wir die map()
Methode von Array
, um jeden Wert im ursprünglichen Array zu verdoppeln:
const originals = [1, 2, 3];
const doubled = originals.map(item => item * 2);
console.log(doubled); // [2, 4, 6]
Die map()
-Methode übergibt jedes Element im Array nacheinander an die gegebene Funktion. Diese nimmt den von dieser Funktion zurückgegebenen Wert und fügt ihn einem neuen Array hinzu.
Im obigen Beispiel ist also item => item * 2
das Pfeilfunktionsäquivalent von:
function doubleItem(item) {
return item * 2;
}
Sie können dieselbe kompakte Syntax verwenden, um das addEventListener
-Beispiel umzuschreiben.
textBox.addEventListener("keydown", (event) =>
console.log(`You pressed "${event.key}".`),
);
Hierbei wird der Wert von console.log()
, der undefined
ist, implizit aus der Rückruffunktion zurückgegeben.
Wir empfehlen, Pfeilfunktionen zu verwenden, da sie Ihren Code kürzer und lesbarer machen können. Um mehr zu erfahren, lesen Sie den Abschnitt über Pfeilfunktionen im JavaScript-Leitfaden und unsere Referenzseite zu Pfeilfunktionen.
Hinweis: Es gibt einige subtile Unterschiede zwischen Pfeilfunktionen und normalen Funktionen. Sie liegen außerhalb des Rahmens dieses Einführungstutorials und werden in den hier diskutierten Fällen wahrscheinlich keinen Unterschied machen. Um mehr zu lernen, sehen Sie die Referenzdokumentation zu Pfeilfunktionen.
Live-Beispiel einer Pfeilfunktion
Hier ist ein vollständiges, funktionierendes Beispiel des "keydown"-Beispiels, das wir oben diskutiert haben:
Das HTML:
<input id="textBox" type="text" />
<div id="output"></div>
Das JavaScript:
const textBox = document.querySelector("#textBox");
const output = document.querySelector("#output");
textBox.addEventListener("keydown", (event) => {
output.textContent = `You pressed "${event.key}".`;
});
Das Ergebnis - versuchen Sie, in das Textfeld zu tippen und sehen Sie sich die Ausgabe an:
Funktionsscope und Konflikte
Sprechen wir ein wenig über den Scope – ein sehr wichtiges Konzept im Umgang mit Funktionen. Wenn Sie eine Funktion erstellen, sind die darin definierten Variablen und andere Dinge in ihrem eigenen, separaten Scope, was bedeutet, dass sie in ihren eigenen, separaten Kompartimenten eingeschlossen sind, die von Code außerhalb der Funktionen nicht erreichbar sind.
Das oberste Niveau außerhalb aller Ihrer Funktionen wird der globale Scope genannt. Im globalen Scope definierte Werte sind von überall im Code zugänglich.
JavaScript ist aus verschiedenen Gründen so aufgebaut – hauptsächlich jedoch aus Gründen der Sicherheit und Organisation. Manchmal möchten Sie nicht, dass Variablen von überall im Code aus zugänglich sind – externe Skripte, die Sie von anderswo aufrufen, könnten anfangen, mit Ihrem Code zu interferieren und Probleme verursachen, weil sie zufällig dieselben Variablennamen wie andere Teile des Codes verwenden, was Konflikte verursacht. Dies könnte absichtlich oder nur versehentlich geschehen.
Zum Beispiel, sagen wir, Sie haben eine HTML-Datei, die zwei externe JavaScript-Dateien aufruft, und beide haben eine Variable und eine Funktion definiert, die denselben Namen verwenden:
<!-- Excerpt from my HTML -->
<script src="first.js"></script>
<script src="second.js"></script>
<script>
greeting();
</script>
// first.js
const name = "Chris";
function greeting() {
alert(`Hello ${name}: welcome to our company.`);
}
// second.js
const name = "Zaptec";
function greeting() {
alert(`Our company is called ${name}.`);
}
Sie werden sehen, dass das zweite Skript überhaupt nicht geladen und ausgeführt wird, und ein Fehler wird in der Konsole ausgegeben: Uncaught SyntaxError: Identifier 'name' has already been declared
. Das liegt daran, dass die name
Konstante bereits in first.js
deklariert ist und Sie dieselbe Konstante nicht zweimal im selben Scope deklarieren können. Da das zweite Skript nicht geladen wurde, ist die greeting()
Funktion aus second.js
nicht verfügbar, um aufgerufen zu werden. Daher wird ein Alarmfeld angezeigt, das Hello Chris: welcome to our company.
anzeigt.
Entfernen Sie die zweite const name = "Zaptec";
-Zeile von second.js
und laden Sie die Seite neu. Jetzt werden beide Skripte ausgeführt, und das Alarmfeld zeigt Our company is called Chris.
an. Funktionen dürfen neu deklariert werden, und die letzte Deklaration wird verwendet. Die vorherigen Deklarationen werden effektiv überschrieben.
Hinweis: Sie können dieses Beispiel live auf GitHub ausführen (siehe auch den Quellcode).
Das Halten von Teilen Ihres Codes in Funktionen vermeidet solche Probleme und wird als Best Practice angesehen.
Es ist ein bisschen wie in einem Zoo. Die Löwen, Zebras, Tiger und Pinguine werden in ihren eigenen Gehegen gehalten und haben nur Zugang zu den Dingen innerhalb ihrer Gehege – ähnlich wie die Funktionsscopes. Wären sie in der Lage, in andere Gehege zu gelangen, würden Probleme auftreten. Im besten Fall würden sich die verschiedenen Tiere in unbekannten Lebensräumen wirklich unwohl fühlen — ein Löwe oder Tiger würde sich im wässrigen, eisigen Reich der Pinguine furchtbar fühlen. Im schlimmsten Fall könnten die Löwen und Tiger versuchen, die Pinguine zu fressen!
Der Zoowärter ist wie der globale Scope — er hat die Schlüssel, um jedes Gehege zu betreten, Futter aufzufüllen, kranke Tiere zu versorgen, usw.
Aktives Lernen: Spielen mit dem Scope
Schauen wir uns ein echtes Beispiel an, um den Scope zu demonstrieren.
-
Erstellen Sie zuerst eine lokale Kopie unseres function-scope.html-Beispiels. Es enthält zwei Funktionen namens
a()
undb()
, und drei Variablen —x
,y
undz
— von denen zwei innerhalb der Funktionen und eine im globalen Scope definiert sind. Es enthält auch eine dritte Funktion namensoutput()
, die einen einzelnen Parameter nimmt und ihn in einem Absatz auf der Seite ausgibt. -
Öffnen Sie das Beispiel in einem Browser und in Ihrem Texteditor.
-
Öffnen Sie die JavaScript-Konsole in Ihren Browser-Entwicklertools. Geben Sie in der JavaScript-Konsole den folgenden Befehl ein:
jsoutput(x);
Sie sollten den Wert der Variablen
x
im Browserfenster angezeigt bekommen. -
Versuchen Sie nun, Folgendes in Ihre Konsole einzugeben
jsoutput(y); output(z);
Beide sollten einen Fehler in die Konsole werfen, der in etwa so lautet: "ReferenceError: y is not defined". Warum ist das so? Aufgrund des Funktionsscope sind
y
undz
in dena()
- undb()
-Funktionen eingeschlossen, sodassoutput()
nicht darauf zugreifen kann, wenn es vom globalen Scope aus aufgerufen wird. -
Was aber, wenn es von innerhalb einer anderen Funktion aufgerufen wird? Versuchen Sie,
a()
undb()
so zu bearbeiten, dass sie wie folgt aussehen:jsfunction a() { const y = 2; output(y); } function b() { const z = 3; output(z); }
Speichern Sie den Code und laden Sie ihn in Ihrem Browser neu, und versuchen Sie dann, die
a()
- undb()
-Funktionen von der JavaScript-Konsole aus aufzurufen:jsa(); b();
Sie sollten die Werte von
y
undz
im Browserfenster angezeigt bekommen. Dies funktioniert einwandfrei, daoutput()
innerhalb der anderen Funktionen aufgerufen wird – im selben Scope, in dem die Variablen, die es in jedem Fall ausgibt, definiert sind.output()
selbst ist von überall verfügbar, da es im globalen Scope definiert ist. -
Versuchen Sie nun, Ihren Code wie folgt zu aktualisieren:
jsfunction a() { const y = 2; output(x); } function b() { const z = 3; output(x); }
-
Speichern und laden Sie die Seite erneut und versuchen Sie es erneut in Ihrer JavaScript-Konsole:
jsa(); b();
Beide
a()
- undb()
-Aufrufe sollten den Wert vonx
im Browserfenster ausgeben. Diese funktionieren einwandfrei, daoutput()
-Aufrufe nicht im selben Scope wiex
definiert sind,x
jedoch eine globale Variable ist, also überall im Code verfügbar ist. -
Versuchen Sie schließlich, Ihren Code so zu aktualisieren:
jsfunction a() { const y = 2; output(z); } function b() { const z = 3; output(y); }
-
Speichern und laden Sie die Seite erneut und versuchen Sie es erneut in Ihrer JavaScript-Konsole:
jsa(); b();
Dieses Mal werfen die
a()
- undb()
-Aufrufe jenen lästigen ReferenceError: variable name is not defined in die Konsole — das liegt daran, dass dieoutput()
-Aufrufe und die Variablen, die sie auszugeben versuchen, nicht im gleichen Funktionsscope sind — die Variablen sind für diese Funktionsaufrufe praktisch unsichtbar.
Hinweis:
Dieselben Scope-Regeln gelten nicht für Schleifen (z. B. for() { }
) und Bedingungsblöcke (z. B. if () { }
) — sie sehen sehr ähnlich aus, sind aber nicht dasselbe! Achten Sie darauf, diese nicht zu verwechseln.
Hinweis: Der ReferenceError: "x" is not defined Fehler ist einer der häufigsten, auf den Sie stoßen werden. Wenn Sie diesen Fehler erhalten und sicher sind, dass Sie die fragliche Variable definiert haben, überprüfen Sie, in welchem Scope sie sich befindet.
Testen Sie Ihr Können!
Sie haben das Ende dieses Artikels erreicht, aber können Sie sich die wichtigsten Informationen merken? Sie können einige weitere Tests finden, um zu überprüfen, ob Sie diese Informationen behalten haben, bevor Sie weitergehen – siehe Testen Sie Ihr Können: Funktionen. Diese Tests erfordern Fähigkeiten, die in den nächsten zwei Artikeln behandelt werden, deshalb möchten Sie diese vielleicht zuerst lesen, bevor Sie es versuchen.
Zusammenfassung
Dieser Artikel hat die grundlegenden Konzepte hinter Funktionen erkundet und bereitet den Weg für den nächsten, in dem wir praktisch werden und Sie durch die Schritte zum Aufbau Ihrer eigenen benutzerdefinierten Funktion führen.
Siehe auch
- Detaillierter Leitfaden zu Funktionen — behandelt einige fortgeschrittene Funktionen, die hier nicht enthalten sind.
- Funktionsreferenz
- Verwendung von Funktionen, um weniger Code zu schreiben, Scrimba MDN-lernt Partner - Eine interaktive Lektion, die eine nützliche Einführung in Funktionen bietet.