10 Funktionsarten und Parameter

Funktionen sind ein grundlegender Baustein der JavaScript-Programmierung. Sie ermöglichen die Strukturierung von Code in logische, wiederverwendbare Einheiten und bilden die Basis für komplexere Konzepte wie Objektorientierung und funktionale Programmierung. JavaScript bietet verschiedene Möglichkeiten, Funktionen zu definieren und mit Parametern zu arbeiten, was die Sprache besonders flexibel macht.

10.1 Funktionsdeklaration

Die klassische Art, eine Funktion zu definieren, ist die Funktionsdeklaration (Function Declaration):

function quadrieren(zahl) {
  return zahl * zahl;
}

Besonderheiten der Funktionsdeklaration:

// Funktionsaufruf vor der Definition - funktioniert dank Hoisting
console.log(willkommen("JavaScript")); // "Willkommen, JavaScript!"

function willkommen(name) {
  return "Willkommen, " + name + "!";
}

Funktionsdeklarationen eignen sich besonders für zentrale, allgemeine Funktionen einer Anwendung, die an verschiedenen Stellen verwendet werden.

10.2 Funktionsausdruck

Ein Funktionsausdruck (Function Expression) weist eine Funktion einer Variablen zu:

const quadrieren = function(zahl) {
  return zahl * zahl;
};

Besonderheiten des Funktionsausdrucks:

// Benannter Funktionsausdruck
const fakultät = function berechneF(n) {
  if (n <= 1) return 1;
  return n * berechneF(n - 1); // Rekursive Verwendung des internen Namens
};

console.log(fakultät(5)); // 120
// console.log(berechneF(5)); // ReferenceError - Name nur innerhalb der Funktion verfügbar

Funktionsausdrücke sind flexibler als Funktionsdeklarationen und werden oft für Funktionen verwendet, die nur in einem begrenzten Bereich benötigt werden, wie Callbacks oder Methodendefinitionen.

10.3 Pfeilfunktionen

Mit ES6 (ECMAScript 2015) wurden Pfeilfunktionen (Arrow Functions) eingeführt, die eine kürzere Syntax für Funktionsausdrücke bieten:

// Grundlegende Form
const quadrieren = (zahl) => {
  return zahl * zahl;
};

// Bei einem einzelnen Parameter können die Klammern weggelassen werden
const verdoppeln = zahl => {
  return zahl * 2;
};

// Bei einem einzeiligen Funktionskörper mit direkter Rückgabe können die geschweiften Klammern und das return weggelassen werden
const halbieren = zahl => zahl / 2;

// Bei keinen Parametern sind leere Klammern erforderlich
const zufallszahl = () => Math.random();

// Bei Rückgabe eines Objektliterals in Kurzform sind runde Klammern nötig
const erstellePerson = name => ({ name, alter: 30 });

Besonderheiten der Pfeilfunktionen:

  1. Lexikalisches this: Pfeilfunktionen haben kein eigenes this, sondern übernehmen das this aus dem umgebenden (lexikalischen) Kontext
const counter = {
  count: 0,
  
  // Problematisch mit regulärer Funktion
  incrementBad: function() {
    setTimeout(function() {
      console.log(this.count++); // NaN, da 'this' nicht counter ist
    }, 1000);
  },
  
  // Korrekt mit Pfeilfunktion
  incrementGood: function() {
    setTimeout(() => {
      console.log(this.count++); // 0, dann 1, 2, ...
    }, 1000);
  }
};
  1. Kein eigenes arguments-Objekt
  2. Nicht als Konstruktor verwendbar (kein new möglich)
  3. Keine Methoden-Definitionen in Objektliteralen (wenn this benötigt wird)
  4. Keine Generator-Funktionalität (kein yield)

Pfeilfunktionen eignen sich hervorragend für kurze Callbacks und Funktionen, bei denen das umgebende this beibehalten werden soll, sind aber nicht für alle Anwendungsfälle optimal.

10.4 Methodensyntax in Objektliteralen

ES6 führte eine verkürzte Syntax für Methoden in Objektliteralen ein:

// Vor ES6
const objekt1 = {
  name: "Beispiel",
  grüßen: function() {
    return "Hallo, " + this.name;
  }
};

// Ab ES6 - verkürzte Methodensyntax
const objekt2 = {
  name: "Beispiel",
  grüßen() {
    return "Hallo, " + this.name;
  }
};

Diese Methoden verhalten sich wie reguläre Funktionen mit eigenem this-Kontext, sind aber kürzer zu definieren.

10.5 Generatorfunktionen

Generatorfunktionen, eingeführt in ES6, ermöglichen es, Funktionen zu schreiben, die ihre Ausführung unterbrechen und später fortsetzen können:

function* sequenz() {
  yield 1;
  yield 2;
  yield 3;
}

const generator = sequenz();
console.log(generator.next().value); // 1
console.log(generator.next().value); // 2
console.log(generator.next().value); // 3

Generatorfunktionen werden durch das Sternchen (*) gekennzeichnet und verwenden das yield-Keyword, um Werte zurückzugeben. Sie sind besonders nützlich für die Arbeit mit Sequenzen und asynchronen Abläufen und werden im Kapitel “Generatoren und Iteratoren” ausführlicher behandelt.

10.6 Async-Funktionen

Asynchrone Funktionen, eingeführt in ES2017, bieten eine elegante Syntax für die Arbeit mit Promises:

async function datenLaden() {
  try {
    const response = await fetch('https://api.beispiel.de/daten');
    const daten = await response.json();
    return daten;
  } catch (error) {
    console.error('Fehler beim Laden der Daten:', error);
  }
}

Asynchrone Funktionen werden mit dem async-Keyword markiert und können das await-Keyword verwenden, um auf Promise-Ergebnisse zu warten. Sie werden im Kapitel “Asynchrones JavaScript” detaillierter behandelt.

10.7 Funktionsparameter

JavaScript bietet vielfältige Möglichkeiten, mit Funktionsparametern umzugehen, von grundlegenden bis zu fortgeschrittenen Techniken.

10.7.1 Grundlegende Parameter

In ihrer einfachsten Form deklarieren Funktionen benannte Parameter, die bei Aufruf mit Argumenten gefüllt werden:

function addieren(a, b) {
  return a + b;
}

console.log(addieren(5, 3)); // 8

JavaScript ist bezüglich Parametern flexibel:

function beispiel(a, b) {
  console.log(a, b);
  console.log("Alle Argumente:", arguments);
}

beispiel(1);                // 1 undefined, Arguments: [1]
beispiel(1, 2, 3, 4);       // 1 2, Arguments: [1, 2, 3, 4]

10.7.2 Das arguments-Objekt

In regulären Funktionen (nicht in Pfeilfunktionen) ist ein spezielles arguments-Objekt verfügbar, das einem Array ähnelt und alle übergebenen Argumente enthält:

function summe() {
  let total = 0;
  for (let i = 0; i < arguments.length; i++) {
    total += arguments[i];
  }
  return total;
}

console.log(summe(1, 2, 3, 4, 5)); // 15

Das arguments-Objekt ist jedoch kein echtes Array und besitzt nicht alle Array-Methoden. In modernem JavaScript werden daher Rest-Parameter empfohlen.

10.7.3 Standardparameter (ES6)

Mit ES6 können Standardwerte für Parameter definiert werden, die verwendet werden, wenn kein Argument übergeben wird oder undefined übergeben wird:

function grüßen(name = "Gast", gruß = "Hallo") {
  return `${gruß}, ${name}!`;
}

console.log(grüßen());              // "Hallo, Gast!"
console.log(grüßen("Max"));         // "Hallo, Max!"
console.log(grüßen("Anna", "Hi"));  // "Hi, Anna!"
console.log(grüßen(undefined, "Willkommen")); // "Willkommen, Gast!"

Standardparameter werden nur aktiviert, wenn der Parameter undefined ist - nicht bei null oder anderen “falsy” Werten:

function config(options = {}, debug = false) {
  console.log(options, debug);
}

config(null, 0);  // null 0 (Standardwerte werden nicht verwendet)

Standardparameter können auch Ausdrücke oder frühere Parameter referenzieren:

function bereichVerdoppeln(min = 0, max = min + 10) {
  return [min, max];
}

console.log(bereichVerdoppeln());     // [0, 10]
console.log(bereichVerdoppeln(5));    // [5, 15]

10.7.4 Rest-Parameter (ES6)

Rest-Parameter ermöglichen es, eine beliebige Anzahl von Argumenten als Array zu behandeln:

function summe(...zahlen) {
  return zahlen.reduce((summe, zahl) => summe + zahl, 0);
}

console.log(summe(1, 2, 3));         // 6
console.log(summe(1, 2, 3, 4, 5));   // 15

Rest-Parameter bieten gegenüber dem arguments-Objekt mehrere Vorteile:

function formatieren(prefix, suffix, ...werte) {
  return werte.map(wert => `${prefix}${wert}${suffix}`);
}

console.log(formatieren("<", ">", "a", "b", "c")); // ["<a>", "<b>", "<c>"]

Rest-Parameter müssen der letzte Parameter in der Parameterliste sein.

10.7.5 Destrukturierung in Parametern (ES6)

Objekt- und Array-Destrukturierung kann direkt in der Parameterliste verwendet werden:

// Objektdestrukturierung
function anzeigenPerson({ name, alter = 'unbekannt', beruf = 'unbekannt' }) {
  console.log(`Name: ${name}, Alter: ${alter}, Beruf: ${beruf}`);
}

anzeigenPerson({ name: "Max", alter: 30 }); // Name: Max, Alter: 30, Beruf: unbekannt
anzeigenPerson({ name: "Anna", beruf: "Entwicklerin" }); // Name: Anna, Alter: unbekannt, Beruf: Entwicklerin

// Array-Destrukturierung
function anzeigenKoordinaten([x, y, z = 0]) {
  console.log(`X: ${x}, Y: ${y}, Z: ${z}`);
}

anzeigenKoordinaten([10, 20]); // X: 10, Y: 20, Z: 0

Destrukturierung kann mit Standardwerten für den gesamten Parameter kombiniert werden:

function config({ port = 8080, timeout = 1000 } = {}) {
  console.log(`Port: ${port}, Timeout: ${timeout}`);
}

config(); // Port: 8080, Timeout: 1000 (leeres Standardobjekt wird destrukturiert)
config({ port: 3000 }); // Port: 3000, Timeout: 1000 (fehlender Wert erhält Standard)

10.8 Beste Praktiken

Für die Arbeit mit Funktionen und Parametern haben sich einige Best Practices etabliert:

  1. Wählen Sie die passende Funktionsart:

  2. Setzen Sie sinnvolle Standardparameter:

  3. Begrenzen Sie die Anzahl der Parameter:

// Schlecht: Zu viele Parameter
function createUser(name, email, age, address, phone, isAdmin, language) {
  // ...
}

// Besser: Optionen als Objekt
function createUser({ name, email, age, address, phone, isAdmin = false, language = 'en' } = {}) {
  // ...
}
  1. Benennen Sie Parameter klar und verständlich:

  2. Validieren Sie Parameter bei Bedarf:

function dividieren(a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw new TypeError('Parameter müssen Zahlen sein');
  }
  
  if (b === 0) {
    throw new Error('Division durch Null nicht möglich');
  }
  
  return a / b;
}