Funktionen — wiederverwendbare Codeblöcke
Ein weiteres essenzielles Konzept beim Programmieren sind Funktionen, mit denen Sie ein Stück Code, das eine einzelne Aufgabe ausführt, in einem definierten Block speichern können. Anschließend können Sie diesen Code mit einem einzigen kurzen Befehl aufrufen, anstatt denselben Code mehrfach schreiben zu müssen. In diesem Artikel erkunden wir grundlegende Konzepte hinter Funktionen, wie die grundlegende Syntax, das Aufrufen und Definieren von Funktionen, den Gültigkeitsbereich und Parameter.
Voraussetzungen: | Ein Verständnis von HTML und den Grundlagen von CSS, sowie Vertrautheit mit den JavaScript-Grundlagen, die in vorherigen Lektionen behandelt wurden. |
---|---|
Lernziele: |
|
Wo finde ich Funktionen?
In JavaScript werden Sie Funktionen überall finden. Tatsächlich haben wir im bisherigen Kursverlauf schon Funktionen verwendet, ohne viel darüber zu sprechen. Jetzt ist es an der Zeit, explizit über Funktionen zu sprechen und deren Syntax genauer zu betrachten.
Jedes Mal, wenn Sie eine JavaScript-Struktur verwenden, die ein Paar Klammern ()
enthält — und Sie nutzen nicht eine gängige eingebaute Sprachstruktur wie eine for-Schleife, while- oder do...while-Schleife oder eine if...else-Anweisung — verwenden Sie eine Funktion.
Eingebaute Browser-Funktionen
Wir haben in diesem Kurs oft eingebaute Browser-Funktionen verwendet.
Zum Beispiel jedes Mal, wenn wir eine Textzeichenkette bearbeitet haben:
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 bearbeitet haben:
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 jedes Mal, wenn wir eine Zufallszahl generiert haben:
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 dürfen diese Zeilen gerne in die JavaScript-Konsole Ihres Browsers eingeben, um sich bei Bedarf ihre Funktionalität wieder in Erinnerung zu rufen.
Die JavaScript-Sprache verfügt über viele eingebaute Funktionen, die es Ihnen ermöglichen, nützliche Dinge zu tun, ohne diesen gesamten Code selbst schreiben zu müssen. Tatsächlich kann ein Teil des Codes, den Sie beim Aufruf (ein gehobener Begriff für das Ausführen) einer eingebauten Browserfunktion aufrufen, in JavaScript überhaupt nicht geschrieben werden — viele dieser Funktionen rufen Teile des Hintergrundcodes des Browsers auf, der größtenteils in systemnahen Sprachen wie C++ geschrieben ist, nicht in Websprachen wie JavaScript.
Bedenken Sie, dass einige eingebaute Browserfunktionen nicht Teil der Kern-JavaScript-Sprache sind — einige sind als Teil von Browser-APIs definiert, die auf der Standard-Sprache aufbauen, um noch mehr Funktionalität bereitzustellen (siehe diesen frühen Abschnitt unseres Kurses für weitere Beschreibungen). Wir werden die Verwendung von Browser-APIs in einem späteren Modul genauer betrachten.
Funktionen versus Methoden
Funktionen, die Teil von Objekten sind, werden als Methoden bezeichnet; Sie werden später in diesem Modul mehr über Objekte erfahren. An dieser Stelle wollten wir nur mögliche Verwirrung zwischen Methode und Funktion klären — Sie werden voraussichtlich beide Begriffe verwenden, während Sie die verfügbaren Ressourcen im Web durchsehen.
Der eingebaute Code, den wir bisher verwendet haben, existiert in beiden Formen: Funktionen und Methoden. Sie können die vollständige Liste der eingebauten Funktionen sowie die eingebauten Objekte und deren entsprechende Methoden hier einsehen.
Sie haben im Kurs auch viele benutzerdefinierte Funktionen gesehen — Funktionen, die in Ihrem Code definiert wurden, nicht im Browser. Jedes Mal, wenn Sie einen benutzerdefinierten Namen mit nachfolgenden Klammern gesehen haben, haben Sie eine benutzerdefinierte Funktion verwendet. In unserem Beispiel random-canvas-circles.html (siehe auch den vollständigen Quellcode) aus unserem Artikel über Schleifen haben wir eine benutzerdefinierte draw()
-Funktion eingebaut, die so aussieht:
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. Jedes Mal, wenn wir dies tun möchten, können wir die Funktion so aufrufen:
draw();
anstatt diesen gesamten Code jedes Mal erneut schreiben zu müssen, wenn wir ihn wiederholen möchten. Funktionen können beliebigen Code enthalten — Sie können sogar andere Funktionen innerhalb von Funktionen aufrufen. Die obige Funktion beispielsweise ruft 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, da die eingebaute Funktion des Browsers 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.
Funktionen aufrufen
Sie kennen dies inzwischen wahrscheinlich, aber nur um sicherzugehen: Um eine Funktion nach deren Definition tatsächlich zu verwenden, müssen Sie sie ausführen — oder aufrufen. Dies geschieht, indem der Funktionsname an einer Stelle im Code eingefügt und mit Klammern ergänzt wird.
function myFunction() {
alert("hello");
}
myFunction();
// calls the function once
Hinweis: Diese Form der Funktionserstellung wird auch als Funktionsdeklaration bezeichnet. Sie wird immer „gehoisted“, sodass Sie die Funktion oberhalb ihrer Definition aufrufen können und sie dennoch funktioniert.
Funktionsparameter
Einige Funktionen erfordern Parameter, die beim Aufruf angegeben werden müssen — dies sind Werte, die in die Funktionsklammern eingeschlossen werden müssen, damit die Funktion ihre Aufgabe ordnungsgemäß ausführen kann.
Hinweis: Parameter werden manchmal als Argumente, Eigenschaften oder sogar Attribute bezeichnet.
Ein Beispiel: Die eingebaute Funktion Math.random()
des Browsers benötigt keine Parameter. Wenn sie aufgerufen wird, gibt sie immer eine zufällige Zahl zwischen 0 und 1 zurück:
const myNumber = Math.random();
Die eingebaute String-Funktion replace()
des Browsers benötigt jedoch zwei Parameter — die Teilzeichenkette, die im Hauptstring gefunden werden soll, und die Teilzeichenkette, mit der dieser String ersetzt werden soll:
const myText = "I am a string";
const newString = myText.replace("string", "sausage");
Hinweis: Wenn Sie mehrere Parameter angeben müssen, werden diese durch Kommata getrennt.
Optionale Parameter
Manchmal sind Parameter optional — Sie müssen sie nicht angeben. Wenn Sie dies nicht tun, übernimmt die Funktion in der Regel eine Art von Standardverhalten. Ein Beispiel: Der Parameter der Array-Funktion join()
ist 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'
Wird kein Parameter als Verbindungs-/Trennzeichen angegeben, 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 ein =
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 Arrow Functions
Bis jetzt haben wir eine Funktion wie folgt erstellt:
function myFunction() {
alert("hello");
}
Sie können jedoch auch eine Funktion erstellen, die keinen Namen hat:
(function () {
alert("hello");
});
Dies wird als anonyme Funktion bezeichnet, da sie keinen Namen hat. Sie sehen häufig anonyme Funktionen, wenn von einer Funktion erwartet wird, dass sie eine andere Funktion als Parameter empfängt. In diesem Fall wird die Funktionsparameterübergabe oft als anonyme Funktion vorgenommen.
Hinweis: Diese Form der Funktionserstellung wird auch als Funktionen-Ausdruck bezeichnet. Im Gegensatz zu Funktionsdeklarationen werden Funktionsausdrücke nicht „gehoisted“.
Beispiel für anonyme Funktionen
Angenommen, Sie möchten Code ausführen, wenn der Benutzer in ein Textfeld tippt. Dafür können Sie die Funktion addEventListener()
des Textfeldes aufrufen. Diese Funktion erwartet mindestens zwei Parameter:
- den Namen des Ereignisses, das überwacht werden soll, in diesem Fall
keydown
- eine Funktion, die beim Eintritt des Ereignisses ausgeführt wird.
Wenn der Benutzer eine Taste drückt, ruft der Browser die angegebene Funktion auf und übergibt ihr als Parameter Informationen zu diesem Ereignis, einschließlich der spezifischen 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 eine anonyme Funktion an addEventListener()
übergeben:
textBox.addEventListener("keydown", function (event) {
console.log(`You pressed "${event.key}".`);
});
Arrow Functions
Wenn Sie eine anonyme Funktion wie diese übergeben, gibt es eine alternative Form, die Sie verwenden können, nämlich eine Arrow Function. Statt function(event)
schreiben Sie (event) =>
:
textBox.addEventListener("keydown", (event) => {
console.log(`You pressed "${event.key}".`);
});
Wenn die Funktion nur einen Parameter hat, 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 ist, können Sie auch die geschweiften Klammern und das return
-Schlüsselwort weglassen und die Ausdruckswerte implizit zurückgeben. Im folgenden Beispiel verwenden wir die map()
-Methode eines Arrays, 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 nimmt jedes Element des Arrays nacheinander und übergibt es an die gegebene Funktion. Sie nimmt dann den von dieser Funktion zurückgegebenen Wert und fügt ihn einem neuen Array hinzu.
Im obigen Beispiel ist item => item * 2
das Arrow Function-Äquivalent zu:
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}".`),
);
In diesem Fall wird der Wert von console.log()
, der undefined
ist, implizit aus der Callback-Funktion zurückgegeben.
Wir empfehlen Ihnen, Arrow Functions zu verwenden, da sie Ihren Code kürzer und lesbarer machen können. Mehr dazu erfahren Sie im Abschnitt über Arrow Functions im JavaScript-Leitfaden und auf unserer Referenzseite zu Arrow Functions.
Hinweis: Es gibt subtile Unterschiede zwischen Arrow Functions und normalen Funktionen. Diese liegen außerhalb des Umfangs dieses Einführungstutorials und sind in den hier besprochenen Fällen wahrscheinlich nicht relevant. Mehr erfahren Sie in der Referenzdokumentation zu Arrow Functions.
Live-Beispiel für Arrow Functions
Hier ist ein vollständiges Beispiel für das oben besprochene "keydown"-Beispiel:
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 - probieren Sie aus, in das Textfeld zu tippen und sehen Sie sich die Ausgabe an:
Gültigkeitsbereich und Konflikte
Lassen Sie uns über den Gültigkeitsbereich sprechen — ein sehr wichtiges Konzept im Umgang mit Funktionen. Wenn Sie eine Funktion erstellen, werden die innerhalb der Funktion definierten Variablen und anderen Elemente in einem eigenen Gültigkeitsbereich eingeschlossen. Das bedeutet, dass sie in ihrem eigenen separaten "Container" gesperrt sind und von Code außerhalb der Funktionen nicht erreicht werden können.
Der oberste Gültigkeitsbereich außerhalb aller Ihrer Funktionen wird als globaler Gültigkeitsbereich bezeichnet. Werte, die im globalen Gültigkeitsbereich definiert sind, sind von überall im Code zugänglich.
JavaScript ist aus verschiedenen Gründen so konzipiert — hauptsächlich wegen Sicherheit und Organisation. Manchmal wollen Sie nicht, dass Variablen von überall im Code zugänglich sind — externe Skripte, die Sie von anderswo einbinden, könnten Ihren Code beeinflussen und Probleme verursachen, weil sie zufällig dieselben Variablennamen wie andere Teile des Codes verwenden, was zu Konflikten führt. Dies könnte absichtlich oder zufällig geschehen.
Beispielsweise nehmen Sie an, dass Sie eine HTML-Datei haben, die zwei externe JavaScript-Dateien einbindet, und beide enthalten eine Variable und eine Funktion, 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 es wird ein Fehler in der Konsole angezeigt: Uncaught SyntaxError: Identifier 'name' has already been declared
. Dies liegt daran, dass die Konstante name
bereits in first.js
deklariert wurde, und Sie können dieselbe Konstante nicht zweimal im selben Gültigkeitsbereich deklarieren. Da das zweite Skript nicht geladen wurde, ist die greeting()
-Funktion aus second.js
nicht verfügbar. Daher wird eine Alert-Box angezeigt mit der Meldung Hello Chris: welcome to our company.
.
Entfernen Sie nun die Zeile const name = "Zaptec";
aus second.js
und laden Sie die Seite erneut. Jetzt werden beide Skripte ausgeführt, und die Alert-Box zeigt Our company is called Chris.
an. Funktionen dürfen hingegen erneut deklariert werden, und die zuletzt deklarierte Funktion wird verwendet. Die vorherigen Deklarationen werden effektiv überschrieben.
Hinweis: Sie können dieses Beispiel live auf GitHub ausführen (siehe auch den Quellcode).
Das Einschränken 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 — genau wie bei den Gültigkeitsbereichen der Funktionen. Würden sie in andere Gehege gelangen, könnten Probleme auftreten. Im besten Fall würden sich verschiedene Tiere in unbekannten Habitaten unwohl fühlen — ein Löwe oder Tiger würde sich in der eisigen, wässrigen Umgebung der Pinguine schrecklich fühlen. Im schlimmsten Fall könnten die Löwen und Tiger versuchen, die Pinguine zu fressen!
Der Zoowärter ist wie der globale Gültigkeitsbereich — er hat die Schlüssel, um jedes Gehege zu betreten, Futter nachzufüllen, kranke Tiere zu pflegen usw.
Praktische Übung: Spielen mit Gültigkeitsbereichen
Schauen wir uns ein reales Beispiel an, um den Gültigkeitsbereich zu demonstrieren.
-
Erstellen Sie zuerst eine lokale Kopie unseres function-scope.html-Beispiels. Dieses enthält zwei Funktionen namens
a()
undb()
und drei Variablen —x
,y
undz
— von denen zwei innerhalb der Funktionen definiert sind und eine im globalen Gültigkeitsbereich. Es enthält auch eine dritte Funktion namensoutput()
, die einen einzigen Parameter entgegennimmt 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 den Entwicklertools Ihres Browsers. Geben Sie in der JavaScript-Konsole den folgenden Befehl ein:
jsoutput(x);
Sie sollten den Wert der Variablen
x
in der Browser-Ansicht sehen. -
Probieren Sie nun Folgendes in Ihrer Konsole aus:
jsoutput(y); output(z);
Beide sollten einen Fehler in der Konsole auslösen, ähnlich wie "ReferenceError: y is not defined". Warum ist das so? Wegen des Gültigkeitsbereichs sind
y
undz
innerhalb der Funktionena()
undb()
eingeschlossen, sodassoutput()
sie nicht aufrufen kann, wenn es vom globalen Gültigkeitsbereich aus aufgerufen wird. -
Was passiert aber, wenn es innerhalb einer anderen Funktion aufgerufen wird? Versuchen Sie,
a()
undb()
so zu ändern:jsfunction a() { const y = 2; output(y); } function b() { const z = 3; output(z); }
Speichern Sie den Code und laden Sie ihn erneut im Browser. Rufen Sie dann die Funktionen
a()
undb()
aus der JavaScript-Konsole auf:jsa(); b();
Sie sollten die Werte von
y
undz
in der Browser-Ansicht sehen. Das funktioniert einwandfrei, da dieoutput()
-Funktion innerhalb der anderen Funktionen aufgerufen wird — im selben Bereich, in dem die Variablen definiert sind. -
Versuchen Sie jetzt, Ihren Code wie folgt zu ändern:
jsfunction a() { const y = 2; output(x); } function b() { const z = 3; output(x); }
-
Speichern und laden Sie das Beispiel erneut. Probieren Sie dies erneut in Ihrer Konsole:
jsa(); b();
Sowohl die
a()
- als auch dieb()
-Aufrufe sollten den Wert vonx
in der Browser-Ansicht ausgeben. Dies funktioniert einwandfrei, weilx
eine globale Variable ist und daher überall verfügbar ist. -
Versuchen Sie schließlich, Ihren Code wie folgt zu ändern:
jsfunction a() { const y = 2; output(z); } function b() { const z = 3; output(y); }
-
Speichern und laden Sie das Beispiel erneut. Probieren Sie dies erneut in Ihrer Konsole:
jsa(); b();
Dieses Mal lösen die
a()
- undb()
-Aufrufe denselben lästigen Fehler ReferenceError: variable name is not defined in der Konsole aus. Das liegt daran, dass sich dieoutput()
-Aufrufe und die Variablen, die sie ausgeben möchten, nicht in denselben Funktions-Gültigkeitsbereichen befinden — die Variablen sind für diese Funktionsaufrufe effektiv unsichtbar.
Hinweis:
Die gleichen Gültigkeitsbereichsregeln gelten nicht für Schleifen (z. B. for() { }
) und bedingte Blöcke (z. B. if () { }
) — sie sehen zwar sehr ähnlich aus, sind aber nicht dasselbe! Achten Sie darauf, diese nicht zu verwechseln.
Hinweis: Der Fehler ReferenceError: "x" is not defined ist einer der häufigsten, auf die Sie stoßen werden. Wenn Sie diesen Fehler erhalten und sicher sind, dass Sie die betreffende Variable definiert haben, überprüfen Sie, in welchem Gültigkeitsbereich sie sich befindet.
Testen Sie Ihr Wissen!
Sie haben das Ende dieses Artikels erreicht, aber können Sie sich an die wichtigsten Informationen erinnern? Sie finden einige zusätzliche Tests, um sicherzustellen, dass Sie sich diese Informationen eingeprägt haben, bevor Sie weitermachen — siehe Testen Sie Ihr Wissen: Funktionen. Für diese Tests sind Kenntnisse erforderlich, die in den nächsten beiden Artikeln behandelt werden. Sie möchten diese vielleicht zuerst lesen, bevor Sie sich an die Tests wagen.
Zusammenfassung
In diesem Artikel haben wir die grundlegenden Konzepte hinter Funktionen erkundet und damit den Weg für den nächsten Artikel geebnet, in dem wir Sie praktisch durch die Schritte zum Aufbau Ihrer eigenen benutzerdefinierten Funktion führen.
Siehe auch
- Detaillierter Leitfaden zu Funktionen — behandelt einige erweiterte Funktionen, die hier nicht enthalten sind.
- Funktionen-Referenz
- Using functions to write less code, Scrimba MDN Lernpartner
- : Ein interaktiver Kurs, der eine nützliche Einführung in Funktionen bietet.