Table of Contents

The JavaScript Array unshift() Method  is a built-in JavaScript array method used to add one or more elements to the beginning of an array. It modifies the original array by inserting the new elements at index 0 and shifting the existing elements to higher indexes. The unshift() method returns the new length of the array, making it useful for managing dynamic collections, updating lists, and implementing data structures. In this tutorial, you will learn its syntax, return value, practical examples, how it works with different data types, and its differences from other JavaScript array methods.

Syntax and Formal Specification

The method signature is unshift(element1, element2, ..., elementN). It accepts zero or more arguments, inserts them at the front in the exact order provided, and returns the new length.

javascript
const fruits = ["cherry", "date"];
const newLength = fruits.unshift("apple", "banana");
console.log(fruits);       // ["apple", "banana", "cherry", "date"]
console.log(newLength);    // 4

Parameters

  • element1, ..., elementN — Zero or more values of any type. Each argument is inserted as its own element at the front, preserving the argument order.

  • No fixed arity — unshift() accepts any number of arguments, including none.

javascript
const arr = [1, 2, 3];
console.log(arr.unshift());   // 3 (no change, returns length)
console.log(arr);             // [1, 2, 3]

Return Value

An integer equal to the new length of the array. Because it returns a number, unshift() is not chainable with other array methods.

javascript
const list = [];
list.unshift("first");         // 1
list.unshift("a", "b");        // 3
console.log(list);             // ["a", "b", "first"]

The Generic Algorithm

Like push() and pop(), unshift() is generic. It operates on any object with a numeric length and integer-indexed properties. The specification algorithm, in simplified form:

  1. Let O be the object on which unshift() is invoked (this).

  2. Convert O.length to an integer len.

  3. Let argCount be the number of provided arguments.

  4. If argCount > 0:

    • If len + argCount > Number.MAX_SAFE_INTEGER, throw a TypeError.

    • Move existing elements from the end to make room: for k from len - 1 down to 0, copy O[k] to O[k + argCount].

    • Assign the new arguments to indices 0 through argCount - 1.

  5. Set O.length = len + argCount.

  6. Return the new length.

The backward shift loop is the reason unshift() is expensive: every existing element moves by argCount positions.

javascript
const arrayLike = { 0: "a", 1: "b", length: 2 };
const newLen = Array.prototype.unshift.call(arrayLike, "x", "y");

console.log(newLen);           // 4
console.log(arrayLike[0]);     // "x"
console.log(arrayLike[1]);     // "y"
console.log(arrayLike[2]);     // "a"
console.log(arrayLike[3]);     // "b"
console.log(arrayLike.length); // 4

Strings cannot be used as this because they are immutable; assignments to indexed properties fail or throw in strict mode.

javascript
"use strict";
try {
  Array.prototype.unshift.call("abc", "z");
} catch (e) {
  console.log(e.constructor.name);   // "TypeError"
}

Performance: O(n) Prepend and the Quadratic Trap

unshift() runs in O(n) time per call because every existing element must be shifted one or more positions to the right to make room at the front. Iterating unshift() in a loop produces O(n²) behavior—a quadratic trap that silently degrades performance on large arrays.

javascript
// Slow: O(n) per call, O(n^2) overall
const slow = [];
for (let i = 0; i < 100000; i++) {
  slow.unshift(i);
}

// Fast: O(1) amortized per call
const fast = [];
for (let i = 0; i < 100000; i++) {
  fast.push(i);
}
fast.reverse();   // O(n), still faster than repeated unshift

The difference is dramatic at scale. For 100,000 elements, the unshift() loop performs roughly 5 billion internal assignments cumulatively, while push() + reverse() performs roughly 200,000 total operations. Engines cannot optimize away this cost because the shifting is observable.

unshift() with Spread Syntax

Spread works with unshift() to prepend elements from another array:

javascript
const target = [4, 5, 6];
const source = [1, 2, 3];
target.unshift(...source);
console.log(target);   // [1, 2, 3, 4, 5, 6]

The same argument-count limit applies: spreading a very large array into unshift() throws a RangeError because of the maximum argument count that engines permit.

javascript
const huge = new Array(200000).fill(0);
const target = [];
// target.unshift(...huge);   // RangeError: Maximum call stack size exceeded

For large prepend operations, use concat:

javascript
const result = huge.concat(target);
console.log(result.length);   // 200000

Distinguishing unshift() from push(), concat(), and splice()

  • unshift() — Mutates. Prepends arguments as individual elements. O(n).

  • push() — Mutates. Appends arguments as individual elements. O(1) amortized.

  • concat() — Returns new array. Flattens array arguments one level. O(n + m).

  • splice(0, 0, ...items) — Mutates. Prepends; O(n) with higher constant factors than unshift.

javascript
const base = [3, 4];

const a = base.slice();
a.unshift(1, 2);
console.log(a);           // [1, 2, 3, 4]
console.log(base);        // [3, 4]

const b = base.slice();
b.push(5, 6);
console.log(b);           // [3, 4, 5, 6]

const c = [1, 2].concat(base);
console.log(c);           // [1, 2, 3, 4]
console.log(base);        // [3, 4]

const d = base.slice();
d.splice(0, 0, 1, 2);
console.log(d);           // [1, 2, 3, 4]

For pure prepend, unshift() is faster than splice(0, 0, ...) because splice engages the general-purpose insertion algorithm.

Building a Newest-First Log with JavaScript unshift()

The JavaScript unshift() method is particularly useful when you need to maintain a log in which the newest entry always appears at the beginning of an array. This pattern is commonly used in activity feeds, notification lists, chat histories, audit trails, and change journals.

When a new event occurs, unshift() inserts the event at index 0. All existing elements move to higher indexes, ensuring that the latest activity appears first.

Example: Creating a Newest-First Activity Log

const activityLog = [];

function record(event) {
    activityLog.unshift({
        event: event,
        timestamp: Date.now()
    });

    // Keep only the latest 100 entries
    if (activityLog.length > 100) {
        activityLog.pop();
    }

    return activityLog.length;
}

record("login");
record("view-page");
record("update-profile");

console.log(activityLog.map(item => item.event));

Output:

["update-profile", "view-page", "login"]

How This Code Works

1. Initializing the activity log

const activityLog = [];

An empty array stores all activity records. Each record will contain an event name and a timestamp.

2. Creating the record() function

function record(event) {
    // Add a new activity
}

The record() function accepts an event name whenever a new activity occurs.

3. Inserting the newest event

activityLog.unshift({
    event: event,
    timestamp: Date.now()
});

The unshift() method inserts a new object at the beginning of the array.

Each object contains:

  • event: The name or description of the activity.
  • timestamp: The time when the activity was recorded, represented as milliseconds since the Unix epoch.

Because the new record is inserted at index 0, the newest activity always appears first.

4. Limiting the log size

if (activityLog.length > 100) {
    activityLog.pop();
}

This condition prevents the activity log from growing indefinitely.

When the array contains more than 100 entries, pop() removes the last entry, which is the oldest activity in this newest-first arrangement.

This creates a bounded history that keeps only the latest 100 records.

5. Returning the current log size

return activityLog.length;

The function returns the number of records currently stored in the activity log.

6. Displaying activities

console.log(activityLog.map(item => item.event));

The map() method extracts the event names from the activity objects and creates a new array for display.

The output shows the most recent event first.

unshift() on Frozen and Sealed Arrays

Frozen arrays reject unshift() with a TypeError in strict mode. Sealed arrays accept element prepends because sealing prevents adding new properties but permits modifying existing ones; however, since unshift() adds elements at new indices, sealed arrays also reject unshift() when the array is at capacity:

javascript
"use strict";

const frozen = Object.freeze([1, 2, 3]);
try {
  frozen.unshift(0);
} catch (e) {
  console.log(e.constructor.name);   // "TypeError"
}

const sealed = Object.seal([1, 2, 3]);
try {
  sealed.unshift(0);
} catch (e) {
  console.log(e.constructor.name);   // "TypeError"
}