Funktionen sind Werte
In JavaScript ist eine Funktion ein Wert wie jeder andere. Du kannst sie in einer Variablen speichern, in ein Array packen, an eine andere Funktion übergeben oder aus einer Funktion zurückgeben. Allein diese Tatsache öffnet die Tür zu einem ganzen Programmierstil – der funktionalen Programmierung in JavaScript:
Sobald du dich daran gewöhnt hast, Funktionen als Werte zu behandeln, verlieren Higher-Order Functions in JavaScript ihren Schrecken. Eine Higher-Order Function (auf Deutsch manchmal „Funktion höherer Ordnung") ist schlicht eine Funktion, die entweder eine andere Funktion als Argument entgegennimmt, eine Funktion zurückgibt – oder beides. Mehr steckt nicht dahinter.
Eine Funktion als Argument übergeben
Der häufigste Fall: Eine Funktion nimmt eine Callback-Funktion entgegen und ruft sie für dich auf. Du hast solche Funktionen wahrscheinlich längst benutzt, ohne groß darüber nachzudenken.
forEach ist eine Higher-Order-Funktion – sie nimmt deine Funktion entgegen und ruft sie einmal pro Element auf. setTimeout ist ebenfalls higher-order – sie nimmt deine Funktion und ruft sie nach einer Verzögerung auf. Du kümmerst dich nur darum, was passieren soll; um das wann und wie oft kümmern sich die Funktionen selbst.
Eine eigene Higher-Order-Funktion zu schreiben sieht ganz ähnlich aus. Hier ist ein kleines Beispiel: eine Funktion, die einen Callback nur dann ausführt, wenn eine Bedingung erfüllt ist:
action ist schlicht ein Parameter, der zufällig eine Funktion enthält. Mit action() führst du aus, was übergeben wurde.
Die drei, die du wirklich brauchst: map, filter, reduce
Arrays bringen Higher-Order-Methoden mit, die die meisten for-Schleifen überflüssig machen, die du sonst schreiben würdest. Wenn du diese drei draufhast, wird ein Großteil deines Alltagscodes kürzer und deutlich lesbarer.
map — jedes Element transformieren
map ruft deine Funktion einmal pro Element auf und sammelt die Rückgabewerte in einem neuen Array. Gleiche Länge, aber transformierter Inhalt. Das ursprüngliche Array bleibt dabei unverändert.
filter – nur das behalten, was passt
filter behält alle Elemente, für die der Callback einen truthy-Wert zurückgibt, und verwirft den Rest. Als Rückgabe bekommst du ein neues Array – unter Umständen kürzer als das ursprüngliche.
reduce – das Array zu einem einzigen Wert zusammenfalten
reduce ist das Schweizer Taschenmesser unter den Array-Methoden. Der Callback bekommt den aktuellen Akkumulator und das jeweilige Element übergeben – was du zurückgibst, wird zum neuen Akkumulator. Das zweite Argument (hier 0) legt den Startwert fest.
Diese Methoden lassen sich verketten. Und genau hier spielt der funktionale Stil seine Stärken aus:
Einfach von oben nach unten lesen: bezahlte Bestellungen filtern, den Preis rausziehen, alles aufsummieren. Keine Schleife, kein veränderlicher Zähler, kein Ärger mit Off-by-one-Fehlern.
Eine Funktion aus einer Funktion zurückgeben
Die andere Hälfte von Higher-Order. Eine Funktion, die eine weitere Funktion baut und zurückgibt:
multiplyBy(2) wird einmal ausgeführt und liefert dir eine brandneue Funktion zurück. Diese neue Funktion merkt sich den Wert von factor weiterhin – das ist eine Closure, der später noch ein eigenes Kapitel gewidmet ist. Für den Moment reicht diese Erkenntnis: Wenn du multiplyBy mit unterschiedlichen Argumenten aufrufst, bekommst du unterschiedliche spezialisierte Funktionen, die alle aus derselben Vorlage entstehen.
Dieses Muster begegnet dir ständig:
Eine Definition, zwei wiederverwendbare Funktionen. Deutlich besser, als warn und info von Hand zu schreiben und sie synchron zu halten.
Benannte Funktionen vs. Inline-Callbacks
Du kannst entweder eine Inline-Arrow-Function übergeben oder eine Funktion per Name – beides funktioniert. Nimm einfach das, was sich besser liest:
Wenn du isEven (ohne Klammern) übergibst, reichst du die Funktion selbst weiter. Schreibst du dagegen () dahinter, wird sie sofort ausgeführt und du übergibst nur noch das Ergebnis – ein klassischer Anfängerfehler:
nums.filter(isEven); // richtig: übergibt die Funktion
nums.filter(isEven()); // falsch: ruft isEven ohne Argumente auf und übergibt das Ergebnis
Sobald der Callback über ein paar Zeilen hinauswächst, lagere ihn aus und gib ihm einen aussagekräftigen Namen. Der umgebende Code liest sich danach meist deutlich angenehmer.
Ein durchgängiges Beispiel
Higher-Order Functions spielen ihre Stärken aus, wenn du kleine Bausteine miteinander kombinierst. Stell dir vor, du hast eine Produktliste und möchtest die Namen aller günstigen, verfügbaren Artikel – und zwar in Großbuchstaben:
Jede Hilfsfunktion hat genau eine Aufgabe. Jede Array-Methode steht für eine Transformation. Die Pipeline liest sich wie eine Beschreibung dessen, was du willst – nicht, wie geschleift werden soll.
Wann du sie lieber nicht einsetzt
Higher Order Functions in JavaScript sind eine feine Sache – aber sie sind nicht für jede Situation der passende Ersatz für eine Schleife:
- Wenn du mittendrin aussteigen willst, ist eine
for- oderfor...of-Schleife mitbreakdeutlich klarer, als sich mühsam aus einemforEachherauszuwinden. - Wenn im Callback etwas Asynchrones passiert, warten
mapundforEachnicht darauf. Nimm stattdessenfor...ofmitawaitoder kombinieremapmitPromise.all. - Wenn der Callback gemeinsam genutzten Zustand verändert, bewegst du dich weg von den Stärken dieses Stils. Dann lieber eine klassische Schleife nehmen – oder so umbauen, dass neue Werte zurückgegeben werden.
Richtig eingesetzt nehmen map, filter und reduce dem Alltagscode das meiste Schleifen-Boilerplate ab. Überall erzwungen, leidet irgendwann die Lesbarkeit. Wähle das Werkzeug, bei dem die Absicht am klarsten rüberkommt.
Weiter geht's: Objekte
Funktionen sind nicht die einzigen Werte, auf denen sich aufbauen lässt. Objekte sind in JavaScript das Arbeitstier, wenn es darum geht, zusammengehörige Daten und Verhalten zu bündeln – und genau daraus bestanden die meisten Arrays, die du gerade gefiltert und gemappt hast. Darum geht's auf der nächsten Seite.
Häufig gestellte Fragen
Was ist eine Higher-Order Function in JavaScript?
Eine Higher-Order Function ist eine Funktion, die mindestens eines von zwei Dingen tut: Sie nimmt eine andere Funktion als Argument entgegen, oder sie gibt selbst eine Funktion zurück. Klassiker dafür sind Array.prototype.map, setTimeout und addEventListener – sie alle bekommen einen Callback und rufen ihn für dich auf.
Worin unterscheiden sich map, filter und reduce?
map wandelt jedes Element um und liefert ein neues Array mit gleicher Länge. filter behält nur die Elemente, bei denen der Callback truthy zurückgibt – das Ergebnis kann also kürzer sein. reduce faltet das Array zu einem einzigen Wert zusammen, indem es die Elemente Schritt für Schritt kombiniert. Alle drei sind Higher-Order Functions, die einen Callback erwarten.
Warum sollte man eine Funktion aus einer Funktion zurückgeben?
Um kleine, konfigurierbare Helfer zu bauen, ohne Logik zu duplizieren. Eine Funktion wie multiplyBy(n) gibt eine neue Funktion zurück, die mit n multipliziert – multiplyBy(2) und multiplyBy(10) liefern dir also zwei spezialisierte Funktionen aus einer einzigen Definition. Dieses Muster basiert auf Closures und begegnet dir ständig bei Event-Handlern, Middleware und in Utility-Bibliotheken.