Array.prototype.sort() sortiert ein Array in-place und liefert dasselbe (mutierte) Array zurück. Ohne Argument vergleicht es alle Elemente als Strings — das führt bei Zahlen zu der berüchtigten Reihenfolge [1, 10, 2]. Mit einer Compare-Funktion (a, b) => zahl lässt sich beliebig sortieren: negativ = a vor b, positiv = b vor a, 0 = unverändert. Seit ES2019 ist sort garantiert stabil (Elemente mit gleichem Compare-Wert behalten ihre Reihenfolge). Mit ES2023 liefert toSorted() die immutable Variante, die das Original unangetastet lässt.

Signatur & Default-Verhalten

JavaScript signatur.js
arr.sort(compareFn?: (a: T, b: T) => number): T[]

Ohne compareFn werden alle Elemente in Strings konvertiert und lexikographisch sortiert. Das ist fast nie, was man will:

JavaScript default-falle.js
const zahlen = [10, 1, 100, 2, 25];
zahlen.sort();
console.log(zahlen);
// → [1, 10, 100, 2, 25] — als Strings sortiert: "1" < "10" < "100" < "2"

// Korrekt:
const zahlen2 = [10, 1, 100, 2, 25];
zahlen2.sort((a, b) => a - b);
console.log(zahlen2);
// → [1, 2, 10, 25, 100]
Output
[ 1, 10, 100, 2, 25 ]
[ 1, 2, 10, 25, 100 ]

Faustregel: fast immer eine compareFn übergeben. Default-Sort ist nur korrekt für Strings, die lexikographisch sortiert werden sollen.

Die Compare-Funktion (a, b) => number

Die Compare-Funktion entscheidet pro Paar:

  • negativ: a kommt vor b
  • positiv: b kommt vor a
  • 0: Reihenfolge gleich (stabil seit ES2019)
JavaScript compare.js
// Aufsteigend
const aufst = [3, 1, 4, 1, 5, 9, 2, 6].sort((a, b) => a - b);
console.log(aufst);

// Absteigend
const abst = [3, 1, 4, 1, 5, 9, 2, 6].sort((a, b) => b - a);
console.log(abst);

// Nach String-Länge
const namen = ['Anna', 'Bo', 'Christopher', 'David'];
namen.sort((a, b) => a.length - b.length);
console.log(namen);

// Locale-bewusst (lexicographisch mit Sprache)
const wuerter = ['Äpfel', 'Apfel', 'Zebra', 'Banane'];
wuerter.sort((a, b) => a.localeCompare(b, 'de'));
console.log(wuerter);
Output
[ 1, 1, 2, 3, 4, 5, 6, 9 ]
[ 9, 6, 5, 4, 3, 2, 1, 1 ]
[ 'Bo', 'Anna', 'David', 'Christopher' ]
[ 'Apfel', 'Äpfel', 'Banane', 'Zebra' ]

a - b (Subtraktion) ist das idiomatische Pattern für Numeric-Sort — kürzer als if (a < b) return -1; ....

Object-Sort nach Property

JavaScript object-sort.js
const users = [
    { name: 'Anna', alter: 30 },
    { name: 'Bob',  alter: 25 },
    { name: 'Chris', alter: 40 },
];

// Nach Alter aufsteigend
users.sort((a, b) => a.alter - b.alter);
console.log(users);

// Nach Name alphabetisch
users.sort((a, b) => a.name.localeCompare(b.name));
console.log(users);
Output
[
  { name: 'Bob', alter: 25 },
  { name: 'Anna', alter: 30 },
  { name: 'Chris', alter: 40 }
]
[
  { name: 'Anna', alter: 30 },
  { name: 'Bob', alter: 25 },
  { name: 'Chris', alter: 40 }
]

Mehrfach-Sort — mehrere Kriterien

Eine Compare-Funktion kann mehrere Kriterien kaskadieren — zuerst nach A sortieren, bei Gleichstand nach B.

JavaScript multi-key.js
const data = [
    { abt: 'IT', name: 'Anna' },
    { abt: 'HR', name: 'Chris' },
    { abt: 'IT', name: 'Bob' },
    { abt: 'HR', name: 'Anna' },
];

// Erst nach Abteilung, dann nach Name
data.sort((a, b) => {
    const abt = a.abt.localeCompare(b.abt);
    if (abt !== 0) return abt;
    return a.name.localeCompare(b.name);
});

console.log(data);
Output
[
  { abt: 'HR', name: 'Anna' },
  { abt: 'HR', name: 'Chris' },
  { abt: 'IT', name: 'Anna' },
  { abt: 'IT', name: 'Bob' }
]

Stable Sort (ES2019)

Seit ES2019 garantiert die Spec, dass sort stabil ist. „Stabil" heißt: Elemente, die nach der Compare-Funktion gleich sind, behalten ihre ursprüngliche Reihenfolge.

JavaScript stable.js
const items = [
    { id: 1, gruppe: 'A' },
    { id: 2, gruppe: 'B' },
    { id: 3, gruppe: 'A' },
    { id: 4, gruppe: 'B' },
    { id: 5, gruppe: 'A' },
];

items.sort((a, b) => a.gruppe.localeCompare(b.gruppe));
console.log(items);
// Ergebnis: alle 'A' vor allen 'B', und INNERHALB jeder Gruppe
// bleibt die Original-Reihenfolge: 1, 3, 5 für A; 2, 4 für B
Output
[
  { id: 1, gruppe: 'A' },
  { id: 3, gruppe: 'A' },
  { id: 5, gruppe: 'A' },
  { id: 2, gruppe: 'B' },
  { id: 4, gruppe: 'B' }
]

Vor ES2019 war dieses Verhalten Engine-abhängig — V8 war instabil bei kleinen Arrays, andere Engines stabil. Heute überall verlässlich.

toSorted() — immutable (ES2023)

toSorted() macht semantisch dasselbe wie sort(), lässt aber das Original unangetastet und liefert ein neues Array.

JavaScript tosorted.js
const original = [3, 1, 4, 1, 5, 9, 2, 6];

const sortiert = original.toSorted((a, b) => a - b);
console.log(original);   // [3, 1, 4, 1, 5, 9, 2, 6] — unverändert
console.log(sortiert);   // [1, 1, 2, 3, 4, 5, 6, 9]

console.log(original === sortiert); // false
Output
[ 3, 1, 4, 1, 5, 9, 2, 6 ]
[ 1, 1, 2, 3, 4, 5, 6, 9 ]
false

Browser-Support: Chrome 110+, Firefox 115+, Safari 16+, Node 20+ — Baseline 2024. Polyfill: arr.slice().sort(...).

Compare-Funktion-Fallen

Falle 1: a > b statt Number-Subtraktion. Liefert Boolean — wird zu 0/1 gecastet, nie negativ.

JavaScript falle-boolean.js
const x = [3, 1, 2].sort((a, b) => a > b);   // Boolean!
console.log(x);   // unspezifiziert, oft falsch

// Korrekt:
const y = [3, 1, 2].sort((a, b) => a - b);
console.log(y);
Output
[ 3, 1, 2 ]
[ 1, 2, 3 ]

Falle 2: Floating-Point-Subtraktion bei sehr großen oder sehr kleinen Zahlen.

JavaScript falle-precision.js
// Beide sind „groß" — Subtraktion kann Floats unsauber werden
const arr = [Number.MAX_VALUE, Number.MAX_VALUE - 1];

// Sicherer: explicit Vergleich
arr.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
console.log(arr[0] === Number.MAX_VALUE - 1);
Output
false

Bei normalen Zahlen ist a - b sicher. Bei sehr großen Werten oder gemischten Floats lohnt der Vergleichs-Ausdruck.

Performance & Algorithmus

Die Spec schreibt keinen spezifischen Algorithmus vor — Engines wählen frei. V8 nutzt seit 2018 TimSort (eine adaptive Mischung aus Mergesort und Insertionsort) — stabil und im Durchschnitt schnell für realistische Daten. Komplexität: O(n log n) worst-case.

Bei sehr großen Arrays (>10.000) ist die Compare-Funktion oft der Flaschenhals — sie wird bis zu n log n mal aufgerufen. Optimierungen: einfache compareFn, möglichst billige Property-Reads, vorgemerkte Sort-Keys.

Häufige Stolperfallen

Default-Sort ist lexikographisch — Zahlen werden zu Strings

[10, 1, 2].sort() ergibt [1, 10, 2] — alle Elemente werden in Strings konvertiert. Bei Zahlen IMMER eine compareFn übergeben: (a, b) => a - b.

compareFn muss EINE Zahl zurückgeben, nicht Boolean

(a, b) => a > b liefert Boolean → wird zu 0 oder 1 gecastet, NIE negativ. Sort-Verhalten daher unspezifiziert. Korrekt: a - b oder explicit a < b ? -1 : a > b ? 1 : 0.

sort mutiert — in React/Redux nie direkt

setItems(items.sort(fn)) mutiert items UND gibt die gleiche Referenz zurück. React rendert nicht neu. Korrekt: setItems(items.toSorted(fn)) oder setItems([...items].sort(fn)).

Stable Sort garantiert seit ES2019

Vor ES2019 war V8 instabil bei kleinen Arrays. Heute überall stabil — Elemente mit gleichem Compare-Wert behalten Reihenfolge. Praktisch beim Multi-Key-Sort: nach geringerer Priorität zuerst sortieren, dann nach höherer.

localeCompare für sprachbewussten String-Sort

'Äpfel'.localeCompare('Apfel', 'de') liefert eine korrekte deutsche Sortierung. Default-String-Vergleich nutzt UTF-16 Codepoint-Order — 'Ä' (196) kommt nach 'z' (122). Bei Sprach-Daten daher fast immer localeCompare.

sort.reverse() vs. compareFn umkehren

arr.sort((a, b) => a - b).reverse() hat zwei Operationen + Mutation. arr.sort((a, b) => b - a) hat eine. Performance-marginal, semantisch klarer mit umgekehrter compareFn.

sort + Date: nach Date direkt subtrahieren möglich

arr.sort((a, b) => a.datum - b.datum) mit Date-Objects funktioniert dank valueOf-Conversion zu Millisekunden. Eleganter als String-Vergleich der ISO-Strings.

Schwartzian Transform: precompute sort keys für Performance

Wenn die compareFn teuer ist (z.B. Lowercase-Konvertierung), lohnt sich: erst arr.map(x => [computeKey(x), x]), dann sortieren, dann .map(([_, x]) => x). Reduziert wiederholte Computation in der compareFn.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Arrays

Zur Übersicht