Array.prototype.shift() entfernt das erste Element eines Arrays in-place und liefert es zurück. Alle anderen Elemente rücken um einen Index nach vorn. Bei einem leeren Array gibt shift undefined zurück. Zusammen mit push ergibt sich die klassische FIFO-Queue: hinten anhängen, vorne entnehmen. Wegen des Reindexings ist shift aber O(n) — bei großen Arrays oder Hot-Loops merkbar. Immutable-Alternative: slice(1) oder [, ...rest] = arr-Destructuring.
Signatur & Grundform
arr.shift(): T | undefined // erstes Element oder undefinedconst arr = [1, 2, 3];
const erste = arr.shift();
console.log(erste); // 1
console.log(arr); // [2, 3] — mutiert, Indizes neu
console.log(arr.length); // 21
[ 2, 3 ]
2Auf leerem Array
shift() auf einem leeren Array liefert undefined ohne Fehler.
const leer = [];
const x = leer.shift();
console.log(x); // undefined
console.log(leer.length); // 0undefined
0In Schleifen daher while (arr.length > 0) als Abbruch-Bedingung, nicht while (arr.shift()) — letzteres bricht bei falsy ersten Werten ab.
FIFO-Queue mit push und shift
Die klassische FIFO-Pattern: hinten anhängen mit push, vorne entnehmen mit shift.
const queue = [];
queue.push('A');
queue.push('B');
queue.push('C');
while (queue.length > 0) {
const next = queue.shift();
console.log('Verarbeite:', next);
}Verarbeite: A
Verarbeite: B
Verarbeite: CReihenfolge: First-In, First-Out. Im Gegensatz zum Stack (push/pop) wird der älteste Eintrag zuerst verarbeitet.
Performance — O(n) wegen Reindexing
Wie unshift muss shift jedes verbliebene Element neu indizieren. Bei großen Arrays oder häufigen Shifts in einer Schleife kann das zum Bottleneck werden.
// Demonstration: N Shifts in Folge = O(N²) gesamt
const N = 1000;
const arr = Array.from({ length: N }, (_, i) => i);
console.time('shifts');
while (arr.length) arr.shift();
console.timeEnd('shifts');
// Alternative: Iteration mit Index, statt zu shiften
const arr2 = Array.from({ length: N }, (_, i) => i);
console.time('iter');
for (let i = 0; i < arr2.length; i++) {
// Verarbeite arr2[i]
}
console.timeEnd('iter');shifts: 0.5ms — 2ms (je nach Engine)
iter: 0.05msBei normalen Daten (< 1000 Elementen) unauffällig; bei sehr großen Queues lohnt eine echte Deque-Datenstruktur (Ring-Buffer, Linked-List).
Immutable: slice(1) oder Destructuring
const original = [1, 2, 3, 4];
// slice(1) — neues Array ohne erstes Element
const rest = original.slice(1);
console.log(original); // [1, 2, 3, 4] — unverändert
console.log(rest); // [2, 3, 4]
// Destructuring — erstes + Rest gleichzeitig
const [erste, ...andere] = original;
console.log(erste, andere); // 1 [2, 3, 4]
// toSpliced (ES2023) — generisch
const ohneErstes = original.toSpliced(0, 1);
console.log(ohneErstes); // [2, 3, 4][ 1, 2, 3, 4 ]
[ 2, 3, 4 ]
1 [ 2, 3, 4 ]
[ 2, 3, 4 ]Das Destructuring-Pattern [erste, ...rest] = arr ist die idiomatische Form, wenn man beides braucht — erstes Element und Rest.
Mehrere Shifts in einer Schleife
In Worker-Pattern wird häufig pro Tick ein Job aus einer Queue genommen.
const tasks = ['lade-config', 'connect-db', 'start-server'];
function workerTick() {
const job = tasks.shift();
if (!job) {
console.log('Queue leer');
return;
}
console.log('Tick — bearbeite:', job);
}
workerTick();
workerTick();
workerTick();
workerTick();Tick — bearbeite: lade-config
Tick — bearbeite: connect-db
Tick — bearbeite: start-server
Queue leershift auf Array-Like
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const x = Array.prototype.shift.call(arrayLike);
console.log(x); // 'a'
console.log(arrayLike); // { '0': 'b', '1': 'c', length: 2 }a
{ '0': 'b', '1': 'c', length: 2 }Alternativen zu shift-basierten Queues
Für Massen-Queues empfiehlt sich eine bessere Datenstruktur:
- Ring-Buffer: zwei Indices (read/write), keine Reindex-Operation. O(1).
- Linked-List: jedes Element hat next-Zeiger,
shiftist O(1). - Eingebaute
Setmit Insertion-Order: Iteration in Reihenfolge möglich, aber kein direktes Front-Pop.
// Minimaler Ring-Buffer
class Queue {
constructor() { this.items = []; this.head = 0; }
push(x) { this.items.push(x); }
shift() {
if (this.head >= this.items.length) return undefined;
const v = this.items[this.head++];
// Optional: gelegentlich compact, um Memory freizugeben
if (this.head > 1000) {
this.items = this.items.slice(this.head);
this.head = 0;
}
return v;
}
get length() { return this.items.length - this.head; }
}
const q = new Queue();
q.push('A'); q.push('B'); q.push('C');
console.log(q.shift(), q.shift(), q.shift());A B CFür Anwendungs-Code mit kleinen Queues (< 100 Elemente) ist Array.prototype.shift völlig okay — die Optimierung lohnt sich erst bei massivem Throughput.
Interessantes
shift ist O(n), pop ist O(1)
Beim shift werden alle nachfolgenden Elemente neu indiziert. Bei großen Arrays spürbar — bei normalen Daten unmessbar. Wenn Reihenfolge egal ist, ist pop immer schneller.
shift gibt erstes Element zurück, nicht das Array
Wie pop liefert shift das entfernte Element. Kein Chaining möglich. const first = arr.shift() setzt first auf das Element.
In React/Redux niemals shift — Mutation
setQueue(queue.shift()) ist Anti-Pattern: shift liefert das entfernte Element, nicht das Array, UND mutiert die Quelle. Korrekt: const [job, ...rest] = queue; setQueue(rest);.
while(arr.shift()) bricht bei falsy ersten Werten ab
while (arr.shift()) {...} sieht elegant aus, aber bricht ab, wenn das erste Element 0, '', false, null oder undefined ist. Sicherer: while (arr.length) { const x = arr.shift(); ... }.
Destructuring [erste, ...rest] = arr ist immutable shift
Wenn beides gebraucht wird — erstes Element und Rest-Array — ist Destructuring kompakter als const first = arr[0]; const rest = arr.slice(1);. Original bleibt unverändert.
shift auf sparse Array — Loch oder Wert? Egal
Wenn das erste Element ein Loch ist, liefert shift undefined und reduziert length. Reindex passiert wie bei dense Arrays.
Bei massiven Queues: dedizierter Datentyp
Bei Tausenden von shift/push-Operationen pro Sekunde lohnt sich ein Ring-Buffer oder Linked-List. In normalem Anwendungs-Code mit kleinen Queues ist Array.prototype.shift völlig okay.
V8-Optimierung: bei reinen Number-Arrays kann shift teurer sein
Bei monomorphen Number-Arrays nutzt V8 einen Packed-Storage. Ein shift kann diesen Storage von „packed" auf „holey" umstellen — Performance-Penalty in nachfolgenden Hot-Loops. Bei Performance-kritischem Code: messen.
Weiterführende Ressourcen
Externe Quellen
- Array.prototype.shift() – MDN
- Array.prototype.shift – ECMAScript Spec
- Array.prototype.toSpliced() – MDN