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

JavaScript signatur.js
arr.shift(): T | undefined   // erstes Element oder undefined
JavaScript grundform.js
const arr = [1, 2, 3];
const erste = arr.shift();

console.log(erste);          // 1
console.log(arr);            // [2, 3] — mutiert, Indizes neu
console.log(arr.length);     // 2
Output
1
[ 2, 3 ]
2

Auf leerem Array

shift() auf einem leeren Array liefert undefined ohne Fehler.

JavaScript leer.js
const leer = [];
const x = leer.shift();
console.log(x);              // undefined
console.log(leer.length);    // 0
Output
undefined
0

In 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.

JavaScript fifo.js
const queue = [];
queue.push('A');
queue.push('B');
queue.push('C');

while (queue.length > 0) {
    const next = queue.shift();
    console.log('Verarbeite:', next);
}
Output
Verarbeite: A
Verarbeite: B
Verarbeite: C

Reihenfolge: 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.

JavaScript performance.js
// 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');
Output
shifts: 0.5ms — 2ms (je nach Engine)
iter: 0.05ms

Bei 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

JavaScript immutable.js
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]
Output
[ 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.

JavaScript worker-pattern.js
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();
Output
Tick — bearbeite: lade-config
Tick — bearbeite: connect-db
Tick — bearbeite: start-server
Queue leer

shift auf Array-Like

JavaScript arraylike.js
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 }
Output
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, shift ist O(1).
  • Eingebaute Set mit Insertion-Order: Iteration in Reihenfolge möglich, aber kein direktes Front-Pop.
JavaScript ring-buffer.js
// 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());
Output
A B C

Fü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

/ Weiter

Zurück zu Arrays

Zur Übersicht