In JavaScript sind Funktionen First-Class Citizens — sie werden behandelt wie jeder andere Wert. Sie lassen sich in Variablen speichern, an Funktionen übergeben, von Funktionen zurückgeben, in Arrays oder Objects ablegen und zur Laufzeit erzeugen. Diese Eigenschaft macht Higher-Order Functions möglich (Funktionen, die Funktionen entgegennehmen oder zurückgeben), liefert das Fundament für Callback-Pattern und ermöglicht Function Composition. Dieser Artikel sammelt die Konsequenzen aus diesem einfachen Spec-Detail — und zeigt, wie es die meisten modernen JavaScript-Patterns trägt.
Funktion als Wert
Eine Funktion ist in JavaScript ein Object vom Typ function. Sie kann genauso wie jeder andere Wert behandelt werden.
// 1. In Variable speichern
const verdoppeln = function(x) { return x * 2; };
console.log(verdoppeln(5));
// 2. In Array
const mathFns = [
x => x + 1,
x => x * 2,
x => x * x,
];
console.log(mathFns[2](4)); // 16
// 3. In Object
const ops = {
add: (a, b) => a + b,
mul: (a, b) => a * b,
divide: (a, b) => a / b,
};
console.log(ops.mul(3, 4)); // 12
// 4. Properties an einer Funktion setzen
function gruss() { return 'Hi'; }
gruss.version = '1.0';
console.log(gruss.version); // '1.0'
// 5. Vergleichen via Reference
const a = () => {};
const b = a;
console.log(a === b); // true (gleiche Reference)10
16
12
1.0
trueJedes Verhalten, das man von Objekten kennt, gilt auch für Funktionen — sie sind eine Spezialform mit Aufrufbarkeit.
Higher-Order Functions
Eine Higher-Order Function ist eine Funktion, die mindestens eines der Folgenden tut:
- nimmt eine Funktion als Argument entgegen
- liefert eine Funktion als Rückgabewert
Beides sind direkte Konsequenzen aus First-Class-Status.
// Nimmt eine Funktion entgegen
function dreimal(fn) {
fn();
fn();
fn();
}
dreimal(() => console.log('Tick'));
// Gibt eine Funktion zurück
function multipliziereMit(n) {
return x => x * n;
}
const verdoppeln = multipliziereMit(2);
console.log(verdoppeln(5)); // 10
// Tut beides
function delay(fn, ms) {
return (...args) => setTimeout(() => fn(...args), ms);
}
const verzoegert = delay(console.log, 100);
verzoegert('verzögert');Tick
Tick
Tick
10
verzögertHigher-Order Functions sind das Rückgrat von Array-Methoden, Decorator-Pattern, React-Hooks, Middleware-Systemen, Observable-Libraries.
Built-in Higher-Order: Array-Methoden
Die wichtigste Sammlung von Higher-Order Functions in JavaScript: map, filter, reduce, forEach, find, some, every, flatMap. Alle nehmen eine Funktion als Argument.
const zahlen = [1, 2, 3, 4, 5, 6, 7, 8];
// map: Wert → Wert
const verdoppelt = zahlen.map(n => n * 2);
// filter: Wert → Boolean
const geraden = zahlen.filter(n => n % 2 === 0);
// reduce: aggregieren
const summe = zahlen.reduce((acc, n) => acc + n, 0);
// Kombiniert in Pipeline
const summeGeraderQuadrate = zahlen
.filter(n => n % 2 === 0)
.map(n => n * n)
.reduce((acc, n) => acc + n, 0);
console.log({ verdoppelt, geraden, summe, summeGeraderQuadrate });{
verdoppelt: [ 2, 4, 6, 8, 10, 12, 14, 16 ],
geraden: [ 2, 4, 6, 8 ],
summe: 36,
summeGeraderQuadrate: 120
}Diese Methoden sind die idiomatische Form moderner JavaScript-Programmierung — kompakt, deklarativ, ohne mutierenden State.
Callback-Pattern
Eine Funktion an eine andere übergeben, die sie zu einem bestimmten Zeitpunkt aufruft — Event-Handler, asynchrone APIs, Timer.
// Synchroner Callback
function reicheDurch(wert, callback) {
return callback(wert);
}
console.log(reicheDurch(5, x => x * 10));
// Asynchroner Callback
function nachher(ms, callback) {
setTimeout(callback, ms);
}
nachher(50, () => console.log('Fertig'));
// Klassische Node-Style: error-first callback
function ladeDaten(url, callback) {
// Simuliert: liefert nach 30ms ein Ergebnis
setTimeout(() => {
if (!url) return callback(new Error('Keine URL'));
callback(null, { ok: true, url });
}, 30);
}
ladeDaten('http://example.com', (err, data) => {
if (err) console.log('Fehler:', err.message);
else console.log('Daten:', data);
});50
Fertig
Daten: { ok: true, url: 'http://example.com' }Promises und async/await haben Callbacks in vielen Use-Cases ersetzt — Event-Handler, Stream-Listener, einfache Animationen sind aber weiterhin callback-basiert.
Function Composition
Eine größere Funktion aus kleineren bauen. Klassisch f(g(x)) oder mit compose/pipe deklarativer.
// Klein und benannt
const trimmen = s => s.trim();
const klein = s => s.toLowerCase();
const ohnePunkte = s => s.replace(/\./g, '');
// Direkt verschachtelt (lesbar von innen nach außen)
const verarbeiten = s => ohnePunkte(klein(trimmen(s)));
console.log(verarbeiten(' Hallo.WELT. '));
// Mit pipe — lesbar von links nach rechts
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
const sauber = pipe(trimmen, klein, ohnePunkte);
console.log(sauber(' Hallo.WELT. '));hallowelt
halloweltJede kleine Funktion ist isoliert testbar; Composition verbindet sie zu Pipelines. Das ist der Kern funktionalen Stils.
Decorator-Pattern — Wrappen mit Funktion-zurück
Eine Higher-Order Function, die eine vorhandene Funktion „dekoriert" — mit Logging, Caching, Authentifizierung, Timing.
// Logging-Decorator
function logiere(fn) {
return function(...args) {
console.log('Aufruf:', fn.name, args);
const result = fn(...args);
console.log('Ergebnis:', result);
return result;
};
}
function summe(a, b) { return a + b; }
const summeMitLog = logiere(summe);
summeMitLog(2, 3);Aufruf: summe [ 2, 3 ]
Ergebnis: 5Dieser Pattern ist die Grundlage hinter React-HOCs (Higher-Order Components), Redux-Middleware, Express-Middleware. Alles dasselbe Pattern, nur mit kontext-spezifischer Form.
Funktion zur Laufzeit erzeugen
Funktionen können auch dynamisch erzeugt werden — entweder über Closures (gewählt) oder über new Function() (selten, fast immer als Anti-Pattern).
// Klassisch: Higher-Order-Factory
function erstelleAddierer(n) {
return x => x + n;
}
const plus10 = erstelleAddierer(10);
console.log(plus10(5));
// Selten: new Function (kein Lexical Scope, langsamer, eval-ähnlich)
const f = new Function('a', 'b', 'return a + b;');
console.log(f(2, 3));15
5new Function() ist vom Strict-Mode ausgenommen, hat keinen Zugriff auf den lexikalischen Scope und wird bei jeder Erstellung neu kompiliert — typisches Anti-Pattern. Closures sind in 99% der Fälle die saubere Lösung.
Strategie-Pattern — Funktionen als Konfiguration
Eine Funktion als Konfigurations-Wert zu übergeben ist eine elegante Form, Verhalten zu parametrisieren.
function sortiere(arr, vergleiche) {
return [...arr].sort(vergleiche);
}
const zahlen = [10, 1, 5, 3, 8];
// Aufsteigend
console.log(sortiere(zahlen, (a, b) => a - b));
// Absteigend
console.log(sortiere(zahlen, (a, b) => b - a));
const namen = ['Anna', 'bob', 'Chris'];
// Case-insensitive
console.log(sortiere(namen, (a, b) =>
a.toLowerCase().localeCompare(b.toLowerCase())
));[ 1, 3, 5, 8, 10 ]
[ 10, 8, 5, 3, 1 ]
[ 'Anna', 'bob', 'Chris' ]Array.prototype.sort ist ein lehrreiches Beispiel: dieselbe Funktion sortiert in jeder gewünschten Reihenfolge — die Strategie kommt von außen, als Vergleichs-Funktion.
Event-Listener — Funktionen als Subscribers
Ein klassischer Anwendungsfall: ein Object hält eine Liste von Funktionen, die bei bestimmten Ereignissen aufgerufen werden.
function erstelleEmitter() {
const listeners = new Map();
return {
on(event, fn) {
if (!listeners.has(event)) listeners.set(event, []);
listeners.get(event).push(fn);
},
emit(event, ...args) {
(listeners.get(event) || []).forEach(fn => fn(...args));
},
};
}
const bus = erstelleEmitter();
bus.on('user', user => console.log('User-Event:', user));
bus.on('user', user => console.log('Logged:', user.name));
bus.emit('user', { name: 'Anna', alter: 30 });User-Event: { name: 'Anna', alter: 30 }
Logged: AnnaDasselbe Pattern findet sich in DOM-Events (addEventListener), Node's EventEmitter, RxJS-Observables. Alles möglich, weil Funktionen First-Class sind.
Interessantes
First-Class ist Spec-Eigenschaft, nicht Library-Feature
Es gibt keinen function-Typ-Wrapper oder ähnliches — JavaScript-Funktionen SIND von der Sprache her Objekte mit Aufrufbarkeit. Daher funktioniert fn.foo = 'bar', typeof fn === 'function', und Funktionen können in jeder Collection liegen.
typeof unterscheidet Funktion von Object
Obwohl Funktionen Objekte sind, liefert typeof fn === 'function' — eine Sonder-Regel im typeof-Algorithmus. typeof {} === 'object'. Das ist die einzige Stelle, an der die Sprache zwischen den beiden in einem typeof-Check unterscheidet.
Function-Properties: name, length und beliebige eigene Felder
fn.name, fn.length sind eingebaute Properties. Eigene Properties kann man problemlos setzen. Manche Libraries nutzen das, um Metadaten an Funktionen zu hängen — etwa fn.__cached als Memoization-Marker.
Higher-Order Functions sind in JS Standard, nicht Spezialfall
Anders als in C oder Java, wo Function-Pointers/Functional-Interfaces ein Sonder-Konstrukt sind, ist eine Higher-Order Function in JavaScript die Norm. Array.map, addEventListener, setTimeout, Promise.then — überall.
Composition statt Vererbung — funktionales Idiom
In OO-Sprachen löst man Wiederverwendung typischerweise mit Klassen-Hierarchie. In FP-Stil mit Function Composition: kleine Funktionen, die sich zu größeren zusammenstellen. pipe(a, b, c) statt class C extends B extends A.
Callback-Hell führte zu Promises — und schließlich async/await
Tief verschachtelte Callbacks ('Pyramid of Doom') waren in Pre-Promise-Code ein realer Schmerz. Promises kamen mit ES2015, async/await mit ES2017 — beide sind Verbesserungen, aber Callbacks sind keineswegs „falsch". Für synchrone Higher-Order-Funktionen (map, filter) gibt es ohnehin keine Promises.
React Hooks sind reine Higher-Order-Function-Pattern
useState, useEffect, useMemo — alle sind Funktionen, die Funktionen als Argument nehmen und State/Side-Effects via Closures verwalten. React funktioniert nur, weil JavaScript First-Class Functions hat.
Function-Identität: jeder neue Wert ist eine eigene Funktion
const a = () => 1; const b = () => 1; — a !== b, auch wenn beide identischen Body haben. In React mit useEffect/useMemo führt das oft zu unnötigen Re-Renders, wenn man bei jedem Render eine neue Function-Reference erzeugt. Lösung: useCallback oder die Funktion außerhalb der Component definieren.
Weiterführende Ressourcen
Externe Quellen
- First-class Function – MDN Glossary
- Callback – MDN Glossary
- Higher-order function – Wikipedia
- Function – ECMAScript Spec
- Eric Elliott: Composing Software