Funktionen – wiederverwendbare Codeblöcke
Ein weiteres grundlegendes Konzept beim Programmieren sind Funktionen. Mit ihnen können Sie Code für eine bestimmte Aufgabe in einem definierten Block zusammenfassen und diesen Code bei Bedarf mit einem kurzen Aufruf ausführen – statt ihn jedes Mal erneut zu schreiben. In diesem Artikel behandeln wir grundlegende Konzepte wie die Syntax von Funktionen, ihre Definition und ihren Aufruf sowie Gültigkeitsbereiche und Parameter.
| Voraussetzungen: | Kenntnisse in HTML und den Grundlagen von CSS sowie Vertrautheit mit den JavaScript-Grundlagen aus den vorherigen Lektionen. |
|---|---|
| Lernziele: |
|
Wo finde ich Funktionen?
In JavaScript begegnen Ihnen Funktionen überall. Tatsächlich haben wir im bisherigen Kurs ständig Funktionen verwendet, ohne viel über sie zu sprechen. Jetzt ist es an der Zeit, Funktionen ausdrücklich zu betrachten und ihre Syntax kennenzulernen.
Fast immer, wenn Sie in JavaScript eine Struktur mit einem Klammerpaar – () – verwenden und es sich nicht um eine Sprachkonstruktion wie eine for-Schleife, eine while- oder do...while-Schleife oder eine if...else-Anweisung handelt, verwenden Sie eine Funktion.
Integrierte Browserfunktionen
In diesem Kurs haben wir bereits häufig integrierte Browserfunktionen verwendet.
Zum Beispiel jedes Mal, wenn wir einen Textstring 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 erzeugt 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
Dabei haben wir jeweils eine Funktion verwendet!
Hinweis: Sie können diese Zeilen in die JavaScript-Konsole Ihres Browsers eingeben, um sich bei Bedarf noch einmal mit ihrer Funktionsweise vertraut zu machen.
JavaScript bietet viele integrierte Funktionen, mit denen Sie nützliche Aufgaben erledigen können, ohne den gesamten Code selbst zu schreiben. Ein Teil des Codes, den Sie beim Aufrufen einer integrierten Browserfunktion ausführen, ließe sich nicht einmal in JavaScript schreiben. Viele dieser Funktionen greifen auf internen Browsercode zu, der größtenteils in systemnahen Sprachen wie C++ statt in Websprachen wie JavaScript geschrieben ist.
Beachten Sie, dass nicht alle integrierten Browserfunktionen zum Kern der Sprache JavaScript gehören. Einige sind Teil von Browser-APIs, die die Standardsprache um zusätzliche Funktionen erweitern (weitere Erläuterungen finden Sie in diesem früheren Abschnitt unseres Kurses). Die Verwendung von Browser-APIs sehen wir uns in einem späteren Modul genauer an.
Funktionen und Methoden
Funktionen, die zu Objekten gehören, heißen Methoden. Objekte lernen Sie später in diesem Modul kennen. Zunächst möchten wir nur einen möglichen Unterschied zwischen den Begriffen klären, denn in anderen Webressourcen werden Ihnen beide begegnen.
Der bisher verwendete integrierte Code umfasst sowohl Funktionen als auch Methoden. Eine vollständige Liste der integrierten Funktionen sowie der integrierten Objekte und ihrer Methoden finden Sie in unserer JavaScript-Referenz.
Im bisherigen Kurs haben Sie außerdem viele selbst definierte Funktionen gesehen: Funktionen, die in Ihrem Code und nicht im Browser definiert sind. Immer wenn auf einen selbst gewählten Namen unmittelbar Klammern folgten, haben Sie eine solche Funktion verwendet. Im Beispiel mit den 100 zufälligen Kreisen aus unserem Artikel über Schleifen haben wir eine selbst definierte Funktion draw() verwendet, 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. Wenn wir das erneut tun möchten, können wir die Funktion so aufrufen, statt jedes Mal den gesamten Code neu zu schreiben:
draw();
Funktionen können beliebigen Code enthalten, auch Aufrufe anderer Funktionen. Beispielsweise ruft die oben gezeigte Funktion draw() dreimal die Funktion random() auf. random() wird durch den folgenden Code definiert:
function random(number) {
return Math.floor(Math.random() * number);
}
Wir brauchten diese Funktion, weil die im Browser integrierte Funktion Math.random() nur eine zufällige Dezimalzahl zwischen 0 und 1 erzeugt. Wir wollten dagegen eine zufällige ganze Zahl zwischen 0 und einer angegebenen Zahl.
Funktionen aufrufen
Wahrscheinlich ist Ihnen das inzwischen klar. Der Vollständigkeit halber: Um eine definierte Funktion tatsächlich zu verwenden, müssen Sie sie ausführen, also aufrufen. Dazu schreiben Sie ihren Namen in den Code und setzen Klammern dahinter.
function myFunction() {
alert("hello");
}
myFunction();
// calls the function once
Hinweis: Diese Art, eine Funktion zu erstellen, wird auch Funktionsdeklaration genannt. Funktionsdeklarationen werden immer durch Hoisting verfügbar gemacht. Deshalb können Sie die Funktion bereits oberhalb ihrer Definition aufrufen.
Funktionsargumente und -parameter
Manche Funktionen benötigen beim Aufruf Argumente: Werte, die innerhalb der Klammern stehen müssen, damit die Funktion ihre Aufgabe erfüllen kann.
Häufig hören Sie auch den Begriff Parameter, der besonders in informellen Gesprächen oft gleichbedeutend mit Argumenten verwendet wird. Die Begriffe haben jedoch unterschiedliche Bedeutungen: Parameter sind die Variablen in der Definition einer Funktion. Argumente sind die Werte, die beim Funktionsaufruf für diese Parameter übergeben werden.
Sehen wir uns einige Beispiele an. Die Funktion Math.random() benötigt keine Argumente. Bei jedem Aufruf gibt sie eine Zufallszahl zwischen 0 und 1 zurück:
const myNumber = Math.random();
Die String-Funktion replace() benötigt dagegen zwei Argumente: die im ursprünglichen String zu suchende Zeichenfolge und die Zeichenfolge, durch die sie ersetzt werden soll:
const myText = "I am a string";
const newString = myText.replace("string", "sausage");
Hinweis: Mehrere Parameter oder Argumente trennen Sie durch Kommas.
Optionale Parameter
Manche Parameter sind optional. Beim Aufruf der Funktion müssen Sie dann kein entsprechendes Argument angeben. In diesem Fall verwendet die Funktion üblicherweise einen Standardwert. Beispielsweise 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 Sie kein Argument für das Verbindungs- beziehungsweise Trennzeichen angeben, wird standardmäßig ein Komma verwendet.
Standardwerte für Parameter
Wenn Sie selbst eine Funktion schreiben und optionale Parameter definieren möchten, können Sie einen Standardwert festlegen: Schreiben Sie dazu nach dem Parameternamen = und anschließend den Standardwert.
function hello(name = "Chris") {
console.log(`Hello ${name}!`);
}
hello("Ari"); // Hello Ari!
hello(); // Hello Chris!
Anonyme Funktionen und Arrow-Funktionen
Bisher haben wir Funktionen so erstellt:
function myFunction() {
alert("hello");
}
Sie können aber auch eine Funktion ohne Namen erstellen:
(function () {
alert("hello");
});
Das ist eine anonyme Funktion, weil sie keinen Namen hat. Anonyme Funktionen sehen Sie häufig dort, wo eine Funktion eine andere Funktion als Argument erwartet. In solchen Fällen wird oft eine anonyme Funktion als Argument übergeben.
Hinweis: Diese Art, eine Funktion zu erstellen, wird auch Funktionsausdruck genannt. Anders als Funktionsdeklarationen werden Funktionsausdrücke nicht durch Hoisting verfügbar gemacht.
Beispiel für eine anonyme Funktion
Angenommen, Sie möchten Code ausführen, wenn jemand etwas in ein Textfeld eingibt. Dazu können Sie die Funktion addEventListener() des Textfelds aufrufen. Diese Funktion erwartet mindestens zwei Argumente:
- Den Namen des Ereignisses, auf das reagiert werden soll – in diesem Fall
keydown. - Eine Funktion, die beim Eintreten des Ereignisses ausgeführt werden soll.
Wenn eine Taste gedrückt wird, ruft der Browser die angegebene Funktion auf und übergibt ihr einen Parameter mit Informationen über das Ereignis, darunter die gedrückte Taste:
function logKey(event) {
console.log(`You pressed "${event.key}".`);
}
textBox.addEventListener("keydown", logKey);
Statt eine separate Funktion logKey() zu definieren, können Sie eine anonyme Funktion an addEventListener() übergeben:
textBox.addEventListener("keydown", function (event) {
console.log(`You pressed "${event.key}".`);
});
Arrow-Funktionen
Für eine solche anonyme Funktion gibt es eine alternative Schreibweise: die Arrow-Funktion. Statt function(event) schreiben Sie (event) =>:
textBox.addEventListener("keydown", (event) => {
console.log(`You pressed "${event.key}".`);
});
Wenn die Funktion nur ein Argument entgegennimmt, können Sie die Klammern darum weglassen:
textBox.addEventListener("keydown", event => {
console.log(`You pressed "${event.key}".`);
});
Wenn Ihre Funktion nur aus einer einzigen Zeile mit einer return-Anweisung besteht, können Sie außerdem die geschweiften Klammern und das Schlüsselwort return weglassen. Der Ausdruck wird dann implizit zurückgegeben. Im folgenden Beispiel verwenden wir die Methode map() von Array, um jeden Wert des ursprünglichen Arrays zu verdoppeln:
const originals = [1, 2, 3];
const doubled = originals.map(item => item * 2);
console.log(doubled); // [2, 4, 6]
Die Methode map() übergibt jedes Element des Arrays an die angegebene Funktion, nimmt deren Rückgabewert entgegen und fügt ihn einem neuen Array hinzu.
Die Arrow-Funktion ist sehr knapp. Mit einer gewöhnlichen anonymen Callback-Funktion sähe unser map()-Code so aus:
const doubled = originals.map(function (item) {
return item * 2;
});
Mit derselben kompakten Syntax können Sie auch das Beispiel mit addEventListener() umschreiben:
textBox.addEventListener("keydown", (event) =>
console.log(`You pressed "${event.key}".`)
);
In diesem Fall wird der Rückgabewert von console.log(), nämlich undefined, implizit von der Callback-Funktion zurückgegeben.
Wir empfehlen Arrow-Funktionen, weil sie Ihren Code kürzer und besser lesbar machen können. Weitere Informationen finden Sie im Abschnitt über Arrow-Funktionen im JavaScript-Leitfaden und auf unserer Referenzseite zu Arrow-Funktionen.
Hinweis: Zwischen Arrow-Funktionen und gewöhnlichen Funktionen gibt es einige subtile Unterschiede. Sie gehen über den Rahmen dieser Einführung hinaus und dürften in den hier besprochenen Fällen keine Rolle spielen. Weitere Informationen finden Sie in der Referenzdokumentation zu Arrow-Funktionen.
Interaktives Beispiel für eine Arrow-Funktion
Hier ist eine vollständige, funktionsfähige Version des oben besprochenen keydown-Beispiels:
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 – geben Sie etwas in das Textfeld ein und beobachten Sie die Ausgabe:
Gültigkeitsbereiche von Funktionen und Namenskonflikte
Sprechen wir über Gültigkeitsbereiche, ein wichtiges Konzept beim Umgang mit Funktionen. Wenn Sie eine Funktion erstellen, befinden sich die darin definierten Variablen und anderen Elemente in einem eigenen Gültigkeitsbereich. Sie sind damit von Code außerhalb der Funktion getrennt und können von dort aus nicht erreicht werden.
Die oberste Ebene außerhalb aller Funktionen heißt globaler Gültigkeitsbereich. Dort definierte Werte sind von überall im Code aus zugänglich.
JavaScript funktioniert vor allem aus Sicherheits- und Organisationsgründen so. Manchmal sollen Variablen nicht von überall im Code aus zugänglich sein. Externe Skripte könnten Ihren Code beeinflussen und Probleme verursachen, wenn sie dieselben Variablennamen verwenden. Solche Konflikte können absichtlich oder versehentlich entstehen.
Angenommen, eine HTML-Datei bindet zwei externe JavaScript-Dateien ein. In beiden sind eine Variable und eine Funktion mit jeweils demselben Namen definiert:
<!-- Excerpt from the 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 können sich dieses Beispiel live auf GitHub ansehen (siehe auch den Quellcode). Öffnen Sie es in einem separaten Browser-Tab, bevor Sie die folgende Erklärung lesen.
-
Wenn das Beispiel im Browser angezeigt wird, erscheint zunächst ein Hinweisfenster mit
Hello Chris: welcome to our company.. Das bedeutet, dass der Aufruf vongreeting()im internen Skript die Funktiongreeting()aus der ersten Skriptdatei ausgeführt hat. -
Das zweite Skript wird dagegen überhaupt nicht geladen und ausgeführt. In der Konsole erscheint der Fehler
Uncaught SyntaxError: Identifier 'name' has already been declared. Der Grund: Die Konstantenamewurde bereits infirst.jsdeklariert. Dieselbe Konstante kann im selben Gültigkeitsbereich nicht zweimal deklariert werden. Da das zweite Skript nicht geladen wurde, steht die Funktiongreeting()aussecond.jsnicht für Aufrufe zur Verfügung. -
Wenn wir die Zeile
const name = "Zaptec";aussecond.jsentfernen und die Seite neu laden würden, liefen beide Skripte. Im Hinweisfenster stünde nunOur company is called Chris.. Wird eine Funktion erneut deklariert, gilt die letzte Deklaration in der Reihenfolge des Quellcodes. Die vorherigen Deklarationen werden dadurch praktisch überschrieben.
Wenn Sie Teile Ihres Codes in Funktionen abgrenzen, vermeiden Sie solche Probleme. Das gilt als bewährte Vorgehensweise.
Stellen Sie sich das wie ein Mehrfamilienhaus vor:
-
Jede Wohnung ist privat und nur für die Menschen zugänglich, die darin wohnen. Ähnlich verhält es sich mit dem Gültigkeitsbereich einer Funktion: Code innerhalb einer Funktion kann auf die dort definierten Variablen und Funktionen zugreifen, Code außerhalb dagegen nicht. Hätten alle Zugang zu den Wohnungen der anderen, gäbe es Probleme – Gegenstände könnten verschoben, beschädigt oder gestohlen werden!
-
Das Gebäude kann außerdem Gemeinschaftsbereiche wie einen Pool, einen Fitnessraum oder einen Aufenthaltsraum haben, die allen offenstehen. Das entspricht dem globalen Gültigkeitsbereich: Auf alles, was dort deklariert ist, kann jede Funktion zugreifen. Dass alle die Gemeinschaftsräume nutzen können, ist sinnvoll.
Mit Gültigkeitsbereichen experimentieren
Sehen wir uns ein konkretes Beispiel an, um Gültigkeitsbereiche zu veranschaulichen.
-
Erstellen Sie zunächst eine neue HTML-Datei auf Ihrem lokalen Dateisystem und fügen Sie den folgenden Code ein:
html<!doctype html> <html lang="en-US"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>Function scope example</title> </head> <body> <script> const x = 1; function a() { const y = 2; } function b() { const z = 3; } function output(value) { const para = document.createElement("p"); document.body.appendChild(para); para.textContent = `Value: ${value}`; } </script> </body> </html>Der Code enthält zwei Funktionen namens
a()undb()sowie drei Variablen –x,yundz. Zwei der Variablen sind innerhalb der Funktionen definiert, eine im globalen Gültigkeitsbereich. Außerdem gibt es eine dritte Funktion namensoutput(), die ein Argument entgegennimmt und dessen Wert 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 dort den folgenden Befehl ein:
jsoutput(x);Der Wert der Variablen
xsollte im Browserfenster erscheinen. -
Versuchen Sie nun, Folgendes in die Konsole einzugeben:
jsoutput(y); output(z);Beide Aufrufe sollten in der Konsole einen Fehler wie „ReferenceError: y is not defined“ auslösen. Warum? Wegen des Funktionsgültigkeitsbereichs:
yundzsind auf die Funktionena()undb()beschränkt. Deshalb kannoutput()nicht auf sie zugreifen, wenn die Funktion aus dem globalen Gültigkeitsbereich aufgerufen wird. -
Was passiert aber, wenn
output()innerhalb einer anderen Funktion aufgerufen wird? Ändern Siea()undb()so, 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 die Seite im Browser neu. Rufen Sie dann
a()undb()über die JavaScript-Konsole auf:jsa(); b();Die Werte von
yundzsollten im Browserfenster erscheinen. Das funktioniert, weiloutput()innerhalb der anderen Funktionen aufgerufen wird, also in demselben Gültigkeitsbereich, in dem die auszugebenden Variablen definiert sind.output()selbst ist von überall aus verfügbar, da die Funktion im globalen Gültigkeitsbereich definiert ist. -
Ändern Sie Ihren Code nun wie folgt:
jsfunction a() { const y = 2; output(x); } function b() { const z = 3; output(x); } -
Speichern und laden Sie die Seite erneut. Versuchen Sie dann noch einmal Folgendes in der JavaScript-Konsole:
jsa(); b();Sowohl
a()als auchb()sollten den Wert vonxim Browserfenster ausgeben. Das funktioniert, obwohl die Aufrufe vonoutput()nicht im selben Gültigkeitsbereich stehen wie die Definition vonx:xist eine globale Variable und daher überall im Code verfügbar. -
Ändern Sie Ihren Code abschließend wie folgt:
jsfunction a() { const y = 2; output(z); } function b() { const z = 3; output(y); } -
Speichern und laden Sie die Seite erneut. Versuchen Sie dann noch einmal Folgendes in der JavaScript-Konsole:
jsa(); b();Diesmal lösen die Aufrufe von
a()undb()in der Konsole den lästigen Fehler ReferenceError: variable name is not defined aus. Die Aufrufe vonoutput()befinden sich nicht in denselben Funktionsgültigkeitsbereichen wie die Variablen, die sie ausgeben sollen. Für diese Aufrufe sind die Variablen daher nicht sichtbar.
Hinweis: Der Fehler ReferenceError: "x" is not defined gehört zu den häufigsten Fehlern, denen Sie begegnen werden. Wenn er auftritt und Sie sicher sind, dass Sie die betreffende Variable definiert haben, prüfen Sie, in welchem Gültigkeitsbereich sie sich befindet.
Ein Exkurs zu Gültigkeitsbereichen in Schleifen und bedingten Anweisungen
Bei mit let und const deklarierten Werten funktionieren die Gültigkeitsbereiche innerhalb von bedingten Anweisungen und Schleifen ähnlich wie Funktionsgültigkeitsbereiche. Wenn Sie beispielsweise dem obigen Beispiel die folgenden Blöcke hinzufügen:
if (x === 1) {
const c = 4;
let d = 5;
}
for (let i = 0; i <= 1; i++) {
const e = 6;
let f = 7;
}
Dann würden Aufrufe von output(c), output(d), output(e) oder output(f) denselben zuvor gesehenen Fehler „ReferenceError: [variable-name] is not defined“ auslösen. Die Funktion output() kann nicht auf diese Variablen zugreifen, weil sie auf ihre jeweiligen Gültigkeitsbereiche beschränkt sind.
Das ältere Schlüsselwort var verhält sich anders. Wenn c, d, e und f mit var deklariert würden:
if (x === 1) {
var c = 4;
var d = 5;
}
for (let i = 0; i <= 1; i++) {
var e = 6;
var f = 7;
}
Dann würden ihre Deklarationen durch Hoisting im globalen Gültigkeitsbereich verfügbar. Ihre Ausgabe, beispielsweise mit output(c), würde daher funktionieren. Der Gültigkeitsbereich von Variablen, die innerhalb von Funktionen mit var deklariert werden, bleibt allerdings auf diese Funktionen beschränkt.
Diese Uneinheitlichkeit kann Verwirrung und Fehler verursachen. Sie ist ein weiterer Grund, let und const statt var zu verwenden.
Zusammenfassung
In diesem Artikel haben wir die grundlegenden Konzepte von Funktionen kennengelernt. Damit sind Sie auf den nächsten Artikel vorbereitet, in dem wir eine eigene Funktion Schritt für Schritt erstellen.
Siehe auch
- Ausführlicher Leitfaden zu Funktionen – behandelt einige fortgeschrittene Funktionen, auf die wir hier nicht eingegangen sind.
- Referenz zu Funktionen
- Mit Funktionen weniger Code schreiben, Scrimba MDN-Lernpartner – eine interaktive Lektion mit einer hilfreichen Einführung in Funktionen.