ES2015 brachte drei zusammengehörige Verbesserungen für Funktions-Parameter: Default-Werte ersetzen das alte x = x || 5-Muster, Rest-Parameter ersetzen das arguments-Objekt, Spread im Aufruf ersetzt fn.apply(null, args). Alle drei nutzen die ...-Syntax — Rest in der Parameter-Liste, Spread im Aufruf. Dieser Artikel zeigt die Mechanik jeder Form mit Code, dokumentiert die Eigenheiten (Default-Evaluierung pro Aufruf, Rest muss letzter Parameter sein) und kombiniert die drei in praktischen Mustern.

Default-Parameter — Werte für fehlende Argumente

Hinter = steht der Default-Wert, der greift, wenn das Argument fehlt oder explizit undefined ist.

JavaScript default-grundform.js
function gruss(name = 'Gast', sprache = 'de') {
    return sprache === 'de' ? `Hallo, ${name}!` : `Hello, ${name}!`;
}

console.log(gruss());                  // 'Hallo, Gast!'
console.log(gruss('Anna'));            // 'Hallo, Anna!'
console.log(gruss('Bob', 'en'));       // 'Hello, Bob!'
console.log(gruss(undefined, 'en'));   // 'Hello, Gast!' — undefined triggert Default
console.log(gruss(null));              // 'Hallo, null!' — null ist KEIN Trigger
Output
Hallo, Gast!
Hallo, Anna!
Hello, Bob!
Hello, Gast!
Hallo, null!

Wichtig: nur undefined löst den Default aus, nicht null, nicht 0, nicht ''. Das ist anders als beim alten x || default-Pattern, das auch bei 0 oder '' ersetzt hätte — meist ist die undefined-only-Semantik die gewollte.

Default als Ausdruck — bei jedem Aufruf neu

Der Default kann jeder Ausdruck sein, inklusive Funktions-Aufrufe und Variablen-Referenzen. Wichtig: er wird bei jedem Aufruf neu ausgewertet.

JavaScript default-ausdruck.js
let id = 0;
function naechsteId() { return ++id; }

function erstelleItem(id = naechsteId(), name = 'Item') {
    return { id, name };
}

console.log(erstelleItem());       // { id: 1, name: 'Item' }
console.log(erstelleItem());       // { id: 2, name: 'Item' } — neuer Wert
console.log(erstelleItem(99));     // { id: 99, name: 'Item' } — kein Default
Output
{ id: 1, name: 'Item' }
{ id: 2, name: 'Item' }
{ id: 99, name: 'Item' }

Spätere Default-Werte können sich auf frühere Parameter beziehen:

JavaScript default-referenz.js
function rechteck(breite, hoehe = breite) {
    return breite * hoehe;
}
console.log(rechteck(5));        // 25 — Quadrat
console.log(rechteck(5, 10));    // 50
Output
25
50

Rest-Parameter — variadic Funktionen

...name sammelt alle restlichen Argumente in einem Array. Das ersetzt das alte arguments-Objekt, das nur Array-like war und in Arrow Functions gar nicht existiert.

JavaScript rest-grundform.js
function summe(...zahlen) {
    return zahlen.reduce((acc, n) => acc + n, 0);
}

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

// Mit festen Parametern davor
function log(level, ...rest) {
    console.log(`[${level}]`, ...rest);
}
log('INFO', 'User', 'Anna', 'eingeloggt');
Output
6
15
0
[INFO] User Anna eingeloggt

Rest-Parameter muss der letzte in der Parameter-Liste sein — ein zweiter Rest oder Parameter nach dem Rest ist SyntaxError.

Rest-Parameter vs. arguments

Drei Unterschiede:

  • Rest ist ein echtes Array (.map, .filter, .reduce). arguments ist Array-like — man muss Array.from(arguments) aufrufen.
  • Rest funktioniert auch in Arrow Functions. arguments nicht.
  • Rest sammelt nur die restlichen Argumente — nicht alle. Das ist meist genau das, was man will.
JavaScript rest-vs-arguments.js
// Klassisch mit arguments — umständlich
function alt() {
    const liste = Array.from(arguments);
    return liste.map(x => x * 2);
}
console.log(alt(1, 2, 3)); // [ 2, 4, 6 ]

// Modern mit Rest
function neu(...args) {
    return args.map(x => x * 2);
}
console.log(neu(1, 2, 3)); // [ 2, 4, 6 ]

// Arrow + Rest — geht problemlos
const arrowFn = (...args) => args.map(x => x * 2);
console.log(arrowFn(1, 2, 3));
Output
[ 2, 4, 6 ]
[ 2, 4, 6 ]
[ 2, 4, 6 ]

In neuem Code: immer Rest-Parameter, niemals arguments. Letzteres ist Legacy.

Spread im Funktions-Aufruf

Dieselbe ...-Syntax — diesmal aber im Aufruf, nicht in der Parameter-Liste — entpackt ein Array in einzelne Argumente.

JavaScript spread-aufruf.js
function summe(a, b, c) { return a + b + c; }

const args = [1, 2, 3];

console.log(summe(...args));              // 6 — Array entpackt
console.log(Math.max(...[5, 1, 8, 3, 7])); // 8
console.log(Math.min(...[5, 1, 8, 3, 7])); // 1
Output
6
8
1

Spread ersetzt das alte fn.apply(null, args)-Pattern. Spread ist kürzer, lesbarer und funktioniert auch im new-Aufruf: new MyClass(...args).

Spread + Rest — Reordering und Forwarding

Spread im Aufruf und Rest in der Parameter-Liste passen perfekt zusammen — etwa beim Forwarding von Argumenten an eine andere Funktion.

JavaScript forwarding.js
function summe(...zahlen) {
    return zahlen.reduce((a, b) => a + b, 0);
}

// Wrapper, der Argumente loggt und weiterreicht
function geloggteSumme(...args) {
    console.log('Aufruf mit:', args);
    return summe(...args);
}

console.log(geloggteSumme(1, 2, 3, 4)); // 10

// Reordering
function getauschtSumme(a, b, ...rest) {
    return summe(b, a, ...rest);
}
console.log(getauschtSumme(1, 2, 3, 4)); // 10 — Reihenfolge geändert, Resultat gleich
Output
Aufruf mit: [ 1, 2, 3, 4 ]
10
10

Forwarding ist eines der häufigsten Muster: Decorator-Pattern, Wrapper-Funktionen, Higher-Order-Componenten. Mit Rest+Spread bleibt der Code unabhängig von der genauen Signatur der inneren Funktion.

Destructuring + Default — Named Parameters simulieren

JavaScript hat keine echten Named Parameters wie Python. Eine Konvention erreicht denselben Effekt: Object-Destructuring mit Default-Werten im Parameter-Slot.

JavaScript named-params.js
function erstelleUser({
    name = 'Anonym',
    alter = 0,
    aktiv = true,
    rolle = 'user',
} = {}) {
    return { name, alter, aktiv, rolle };
}

// Alle Defaults
console.log(erstelleUser());

// Einzelne überschreiben — Reihenfolge egal
console.log(erstelleUser({ rolle: 'admin', name: 'Anna' }));

// Nur eine Option
console.log(erstelleUser({ alter: 30 }));
Output
{ name: 'Anonym', alter: 0, aktiv: true, rolle: 'user' }
{ name: 'Anna', alter: 0, aktiv: true, rolle: 'admin' }
{ name: 'Anonym', alter: 30, aktiv: true, rolle: 'user' }

Das = {} am Ende ist wichtig: ohne es würde erstelleUser() versuchen, undefined zu destructurieren — und mit TypeError scheitern. Mit = {} ist der Aufruf ohne Argumente erlaubt.

function.length — Defaults zählen nicht

Die .length-Property einer Funktion gibt die Anzahl der Parameter ohne Default und vor dem ersten Default zurück.

JavaScript function-length.js
function a(x, y, z) {}
function b(x, y = 1, z) {}
function c(x = 1, y, z) {}
function d(...rest) {}
function e(x, ...rest) {}

console.log(a.length); // 3
console.log(b.length); // 1 — alles ab erstem Default wird ignoriert
console.log(c.length); // 0
console.log(d.length); // 0 — Rest zählt nicht
console.log(e.length); // 1
Output
3
1
0
0
1

Praktisch relevant für Reflection-Code (Dependency-Injection-Container, AOP-Frameworks), die auf .length reagieren.

Defaults haben TDZ-ähnliches Verhalten

Default-Werte werden in einer eigenen Scope-Ebene ausgewertet, bevor der Function-Body startet. Spätere Parameter sehen frühere — frühere sehen spätere nicht.

JavaScript default-tdz.js
// OK: y kann auf x verweisen
function ok(x = 1, y = x + 1) {
    return [x, y];
}
console.log(ok());      // [ 1, 2 ]

// Fehler: x verweist auf späteres y → ReferenceError
try {
    function bug(x = y, y = 1) {}
    bug();
} catch (e) {
    console.log('TDZ:', e.message);
}
Output
[ 1, 2 ]
TDZ: Cannot access 'y' before initialization

Das Verhalten ist analog zu let in einem Block — Reihenfolge der Auswertung ist von links nach rechts.

Interessantes

Default greift nur bei undefined, nicht bei null

fn(undefined) löst Default aus, fn(null) nicht — null landet als Wert null. Das ist Spec-Verhalten und unterscheidet sich vom alten x = x || default-Pattern, das auch 0 oder '' als „leer" gewertet hätte.

Default wird PRO Aufruf neu ausgewertet

function f(arr = []) erzeugt bei jedem Aufruf ein neues Array. Anders als in Python (wo der Default geteilt wird und mutable Defaults eine berüchtigte Falle sind), ist JS hier sicher — jeder Aufruf bekommt seinen eigenen Default-Wert.

Rest muss letzter Parameter sein — sonst SyntaxError

function f(...args, last) {} wirft SyntaxError: Rest parameter must be last formal parameter. Klingt trivial, aber bei Refactors leicht übersehen. Logisch: Rest sammelt „alles, was übrig ist" — danach kann definitionsgemäß nichts mehr kommen.

Spread ist nicht nur für Funktionen — auch Array/Object-Literale

const arr2 = [...arr1, 4, 5] und const obj2 = {...obj1, key: 'wert'} nutzen dieselbe Syntax für Array- und Object-Spreading. In Funktions-Aufrufen ist es derselbe Operator, nur in einem anderen Kontext. Object-Spread ist seit ES2018 spezifiziert.

function.length zählt nur bis zum ersten Default

function f(a, b = 1, c) {} hat f.length === 1 — alles ab erstem Default zählt nicht mehr, auch der erforderliche c. Das ist eine Spec-Eigenheit, die Tooling-Autoren überrascht. Wenn man tatsächliche Parameter-Anzahl will: Function-Source parsen oder eigene Metadaten pflegen.

Destructuring im Parameter — kein Default ohne = {}

function f({a, b}) {} wirft TypeError bei Aufruf ohne Argument, weil undefined nicht destructurierbar ist. Lösung: function f({a, b} = {}) {} — der Default {} wird destrukturiert und liefert a, b als undefined. Idiomatischer Pattern für Optionen-Object.

Spread mit non-iterable wirft TypeError

fn(...123) oder fn(...{a:1}) wirft TypeError: x is not iterable. Spread im Funktions-Aufruf nutzt das Iterator-Protokoll — Objekte ohne Symbol.iterator gehen nicht. Object-Spread in {...}-Literalen ist davon verschieden, das nutzt Own-Property-Enumeration.

Default-Wert kann sich auf vorherige Parameter beziehen

function f(a, b = a * 2) ist gültig — b defaultet auf das Doppelte von a. Spätere können auf frühere greifen, aber nicht umgekehrt. Das ist auf TDZ-Mechanik zurückzuführen und erlaubt sinnvolle Pattern wie rechteck(breite, hoehe = breite) für Quadrate als Default.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Funktionen

Zur Übersicht