Der Spread-Operator ... ist eine der wirkungsvollsten Syntax-Ergänzungen aus ES2015. In Array-Literalen entpackt er ein Iterable in einzelne Elemente: [...a, ...b] kombiniert zwei Arrays, [...iter] materialisiert ein Iterable als Array, [...set] entfernt Duplikate. In Funktions-Aufrufen entpackt er ein Array als Argumente: Math.max(...nums). Er ersetzt damit viele ältere Patterns wie Array.prototype.slice.call(), apply() und concat(). Dieser Artikel zeigt die wichtigsten Anwendungs-Muster, klärt die Iterable-Voraussetzung und nennt die feinen Unterschiede zu concat() und Array.from().
Flache Kopie eines Arrays
Der häufigste Anwendungsfall: ein Array klonen, ohne dieselbe Referenz zu teilen.
const original = [1, 2, 3];
const kopie = [...original];
kopie.push(4);
console.log(original); // [1, 2, 3] — unverändert
console.log(kopie); // [1, 2, 3, 4]
console.log(original === kopie); // false — neue Referenz[ 1, 2, 3 ]
[ 1, 2, 3, 4 ]
falseWichtig: flache Kopie. Verschachtelte Objekte werden per Referenz übernommen. Für Deep-Copy: structuredClone(arr) (ES2022).
const inner = { x: 1 };
const original = [inner];
const kopie = [...original];
// Innere Referenz geteilt
inner.x = 99;
console.log(kopie[0].x); // 99 — Mutation propagiert99Arrays kombinieren
[...a, ...b] ist die idiomatische Form von a.concat(b) — kürzer und besser lesbar.
const a = [1, 2];
const b = [3, 4];
const kombiniert = [...a, ...b];
console.log(kombiniert); // [1, 2, 3, 4]
// Beliebig viele Quellen
const c = [5];
const alles = [...a, ...b, ...c, 6, 7];
console.log(alles); // [1, 2, 3, 4, 5, 6, 7]
// Mit Werten zwischendrin
const sandwich = [0, ...a, 'mitte', ...b, 'ende'];
console.log(sandwich);[ 1, 2, 3, 4 ]
[ 1, 2, 3, 4, 5, 6, 7 ]
[ 0, 1, 2, 'mitte', 3, 4, 'ende' ]Iterables entpacken — Set, Map, String, Generator
Spread nutzt das Iterator-Protokoll — alles, was iterable ist, lässt sich spreaden.
// Set → Array (klassisches Dedup-Pattern)
const dedup = [...new Set([1, 2, 2, 3, 3, 3])];
console.log(dedup); // [1, 2, 3]
// String → Array von Codepoints
console.log([...'a😀b']); // ['a', '😀', 'b']
// Map → Array von [key, value]-Tupeln
const m = new Map([['a', 1], ['b', 2]]);
console.log([...m]); // [['a', 1], ['b', 2]]
// Generator
function* gen() { yield 'x'; yield 'y'; yield 'z'; }
console.log([...gen()]); // ['x', 'y', 'z'][ 1, 2, 3 ]
[ 'a', '😀', 'b' ]
[ [ 'a', 1 ], [ 'b', 2 ] ]
[ 'x', 'y', 'z' ]Achtung: Plain-Objects sind nicht iterable. [...{a: 1}] wirft TypeError. Object-Spread {...obj} ist ein anderer Operator (Property-Enumeration, seit ES2018) — nicht zu verwechseln mit Array-Spread.
In Funktions-Aufrufen
Spread im Call-Site entpackt ein Array in einzelne Argumente.
const nums = [5, 1, 8, 3, 9, 2];
// Max/Min: erwarten einzelne Argumente
console.log(Math.max(...nums)); // 9
console.log(Math.min(...nums)); // 1
// Eigene Funktion
function summe(a, b, c) { return a + b + c; }
console.log(summe(...[10, 20, 30])); // 60
// Pre-ES2015 wäre das: Math.max.apply(null, nums)9
1
60Spread ist nicht auf das erste Argument beschränkt — mehrere Spreads und feste Werte können beliebig gemischt werden:
function f(a, b, c, d, e) {
console.log(a, b, c, d, e);
}
const start = [1, 2];
const ende = [4, 5];
f(...start, 3, ...ende);1 2 3 4 5Mit new — Constructor-Aufruf mit Spread
Spread funktioniert auch im new-Aufruf — das war vor ES2015 mit apply gar nicht möglich.
// Date-Konstruktor mit dynamischer Argument-Liste
const args = [2026, 4, 14]; // Jahr, Monat (0-basiert), Tag
const d = new Date(...args);
console.log(d.toISOString().slice(0, 10));
// Eigene Klasse
class Point {
constructor(x, y, z) {
this.x = x; this.y = y; this.z = z;
}
}
const p = new Point(...[1, 2, 3]);
console.log(p);2026-05-14
Point { x: 1, y: 2, z: 3 }Drei Wege im Vergleich
| Operation | Spread [...] | concat | Array.from |
|---|---|---|---|
| Echte Arrays kombinieren | [...a, ...b] | a.concat(b) | nein |
| Set / String / Map / Gen | ja, entpackt | nein (als Element) | ja, entpackt |
| Array-Like (HTMLCollection) | nein (TypeError) | nein | ja |
| Mit Mapper | nein | nein | ja, zweites Argument |
| Sparse Array | füllt mit undefined | erhält Löcher | erhält Löcher |
| Lesbarkeit (kurz) | sehr gut | gut | mittel |
Faustregel: Spread für die meisten Fälle, Array.from für Array-Likes oder mit Mapper, concat nur in Spezialfällen (Sparse-Erhaltung, Symbol.isConcatSpreadable).
Spread vs. Rest — dieselbe Syntax, anderer Kontext
... heißt syntaktisch zwei verschiedene Dinge:
- Spread: in einer Array/Object-Literal oder im Funktions-Aufruf → entpackt.
- Rest: in einer Parameter-Liste oder Destructuring-Muster → sammelt.
// Spread (entpackt)
const arr = [...[1, 2, 3], 4, 5];
console.log(arr); // [1, 2, 3, 4, 5]
// Rest (sammelt) — in Parametern
function f(...args) {
return args;
}
console.log(f(1, 2, 3)); // [1, 2, 3]
// Rest in Destructuring
const [erste, ...andere] = [10, 20, 30, 40];
console.log(erste, andere); // 10 [20, 30, 40][ 1, 2, 3, 4, 5 ]
[ 1, 2, 3 ]
10 [ 20, 30, 40 ]Beide nutzen ... — der Kontext entscheidet, welche Operation gemeint ist.
Sparse-Arrays auffüllen
Wer ein sparse Array hat (z.B. von new Array(n)), kann Spread nutzen, um die Löcher zu undefined zu füllen:
const sparse = new Array(3);
console.log(0 in sparse); // false — Loch
const dense = [...sparse];
console.log(0 in dense); // true — undefined als echter Wert
console.log(dense); // [undefined, undefined, undefined]false
true
[ undefined, undefined, undefined ]Das ist ein subtiler aber wichtiger Unterschied — forEach/map/filter überspringen Löcher, aber durchlaufen undefined-Elemente.
Performance — bei großen Arrays
Spread ist generell etwas langsamer als concat bei sehr großen Quellen, weil es pro Quelle das Iterator-Protokoll durchläuft. Bei normalen Anwendungs-Daten (< 10.000 Elemente) unmessbar.
Bei extrem großen Argument-Listen (Math.max(...arr) mit Millionen Werten) gibt es eine harte Grenze: Engines haben einen Argument-Stack-Limit — Chrome/V8 etwa 65.536 Argumente, dann RangeError. Für solche Fälle ist eine manuelle Reduce-Schleife sicherer:
// Bei sehr großen Arrays — Stack-Overflow möglich
// const max = Math.max(...riesigesArray); // ggf. RangeError
// Sicherer:
function max(arr) {
return arr.reduce((m, x) => x > m ? x : m, -Infinity);
}
console.log(max([5, 1, 8, 3, 9])); // 99Interessantes
Spread basiert auf dem Iterator-Protokoll
Alles, was Symbol.iterator implementiert, lässt sich spreaden. Daher: Array, Set, Map, String, Generator, NodeList. NICHT: Plain-Object, HTMLCollection, Number. Bei Versuch: TypeError: x is not iterable.
Object-Spread {...obj} ist eine andere Operation
{...obj} nutzt nicht das Iterator-Protokoll, sondern own enumerable Properties. Ist seit ES2018 spezifiziert. Daher: [...obj] wirft TypeError für Plain-Objects, {...obj} klappt. Verwirrend, aber gewollt.
Spread im new-Call: nur dank ES2015
new Klasse(...args) war Pre-ES2015 schwer zu implementieren. Function.prototype.apply funktioniert nicht mit new. Workaround damals: Object.create(Klasse.prototype) + manueller Constructor-Aufruf — hässlich. Spread löst das elegant.
Spread füllt Löcher mit undefined, concat erhält sie
[...[1, , 3]] liefert [1, undefined, 3] — Loch wurde gefüllt. [1, , 3].concat() erhält das Loch. In modernem Code irrelevant (sparse Arrays selten), aber bei Legacy-Code mit new Array(n) wichtig.
Dedup-Pattern: [...new Set(arr)]
Die idiomatische Form, Duplikate aus einem Array zu entfernen: [...new Set(arr)]. Set erlaubt keine Duplikate, Spread entpackt es zurück in ein Array. Funktioniert für primitives nahezu perfekt. Bei Object-Werten greift Reference-Equality — gleiche Werte aus verschiedenen Refs werden NICHT als Duplikat erkannt.
Spread vs. Math.max-Limit — RangeError bei zu vielen Argumenten
Math.max(...riesigesArray) wirft bei sehr großen Arrays (V8 ab ~65k Argumenten) einen RangeError: Maximum call stack size exceeded. Für massive Datenmengen: arr.reduce nutzen.
Spread in JSX/Templates für Props-Forwarding
In React: <Component {...props} /> spreaded ein Props-Object in die Component. In Vue/JSX ähnlich. Das ist Object-Spread (nicht Array-Spread), aber dieselbe Syntax. Sehr verbreitet beim Decorator-Pattern.
Mehrfaches Spread ist günstig, mehrfaches concat nicht
[...a, ...b, ...c, ...d] baut das Ergebnis in einer Pass — Engine optimiert. a.concat(b).concat(c).concat(d) würde mehrere Zwischen-Arrays allokieren. Bei massiven Daten messbar; bei normalem Code unwichtig.
Weiterführende Ressourcen
Externe Quellen
- Spread syntax – MDN
- Iteration protocols – MDN
- Rest parameters – MDN
- Spread Operator – ECMAScript Spec