JavaScript Array pop() Method is a built-in JavaScript array method used to remove the last element from an array. It returns the removed element and decreases the original array’s length by one. In this tutorial, you will learn its syntax, return value, practical examples, performance, limitations, and advanced use cases.
The JavaScript Array pop() Method modifies the original array instead of creating a new one. It is commonly used in stack operations, array manipulation, and removing the last item from a collection
Syntax and Formal Specification
The method signature is pop(). It takes no arguments. Any arguments passed are silently ignored.
const numbers = [10, 20, 30]; const removed = numbers.pop(); console.log(removed); // 30 console.log(numbers); // [10, 20]
Parameters
-
None.
pop()accepts no arguments. Passing arguments has no effect and does not throw.
const arr = [1, 2, 3]; arr.pop("ignored", 42); console.log(arr); // [1, 2]
Return Value
-
The last element of the array, if the array is non-empty.
-
undefinedif the array is empty. -
The element is returned before the mutation is visible to the caller, but since the mutation is synchronous, the returned value is the removed element.
const empty = []; console.log(empty.pop()); // undefined console.log(empty.length); // 0 console.log(empty); // []
The Generic Algorithm
Like push(), pop() is generic. It operates on any object with a length property and integer-indexed properties, not just on Array instances. The specification describes it roughly as follows:
-
Let
Obe the object upon whichpop()is invoked (this). -
Read
len=O.length, converted to an integer. -
If
len === 0, setO.length = 0and returnundefined. -
Let
index = len - 1. -
Read
element = O[index]. -
Delete the property
O[index]. -
Set
O.length = index. -
Return
element.
const arrayLike = { 0: "a", 1: "b", 2: "c", length: 3 }; const last = Array.prototype.pop.call(arrayLike); console.log(last); // "c" console.log(arrayLike.length); // 2 console.log(arrayLike[2]); // undefined
This genericity enables pop() to function on arguments objects in legacy code, DOM collections (where they support index and length), and custom collection objects.
function legacy() { const last = Array.prototype.pop.call(arguments); return { last, remaining: arguments.length }; } console.log(legacy(1, 2, 3)); // { last: 3, remaining: 2 }
Mutability and the Immutability Trade-off
pop() is a mutating method. It changes the array’s content and length. When immutability is required, pop() is not appropriate. The immutable counterpart is a slice-based or spread-based reconstruction:
// Mutating (fast, local scope) const mutable = [1, 2, 3]; const removed = mutable.pop(); // Immutable (returns a new array without the last element) const original = [1, 2, 3]; const withoutLast = original.slice(0, -1); console.log(original); // [1, 2, 3] console.log(withoutLast); // [1, 2] // Immutable with spread const [ ...rest, last ] = original; // Not valid — the rest operator must be last. // Use this pattern instead: const copy = [...original]; const lastRemoved = copy.pop();
Immutable Pop Pattern
There is no dedicated immutable pop method. The idiomatic alternatives are:
| Approach | Example | Behavior |
|---|---|---|
slice(0, -1) |
arr.slice(0, -1) |
New array without last element |
| Spread and slice | [...arr].slice(0, -1) |
Same, defensive copy |
| Destructuring with rest | const [head, ...tail] = arr |
Removes from front, not back |
| Manual index access | arr[arr.length - 1] + slice |
Read and rebuild |
const state = [1, 2, 3, 4]; // Read the top without removing (common in reducers) const top = state[state.length - 1]; console.log(top); // 4 // Create a new array without the top const next = state.slice(0, -1); console.log(next); // [1, 2, 3] console.log(state); // [1, 2, 3, 4]
Performance: O(1) Removal and the Deletion Cost
pop() is O(1) on dense arrays. Removing the last element requires no shifting of other elements, and the only observable operation is decrementing length and clearing the final slot. This makes it dramatically faster than shift(), which is O(n) because it must relocate every remaining element.
// Fast: O(1) per operation const stack = []; for (let i = 0; i < 100000; i++) stack.push(i); for (let i = 0; i < 100000; i++) stack.pop(); // cheap // Slow: O(n) per operation, O(n^2) overall const queue = []; for (let i = 0; i < 100000; i++) queue.push(i); for (let i = 0; i < 100000; i++) queue.shift(); // expensive
For queue semantics (FIFO), use pop() combined with reversed insertion order, or maintain a head index over push():
class Queue { constructor() { this.items = []; this.head = 0; } enqueue(x) { this.items.push(x); } dequeue() { if (this.head >= this.items.length) return undefined; return this.items[this.head++]; } get size() { return this.items.length - this.head; } }
Distinguishing pop() from shift() and splice()
-
pop()— Removes last element. O(1). Mutates. -
shift()— Removes first element. O(n). Mutates. Use only for small arrays or unusual cases. -
splice(-1, 1)— Removes last element via general splice. O(1) in the removal cost but higher constant factors thanpop(). Rarely preferable. -
slice(0, -1)— Returns a new array without the last element. Non-mutating.
const a = [1, 2, 3]; console.log(a.pop()); // 3 console.log(a); // [1, 2] const b = [1, 2, 3]; console.log(b.shift()); // 1 console.log(b); // [2, 3] const c = [1, 2, 3]; console.log(c.splice(-1, 1)); // [3] console.log(c); // [1, 2] const d = [1, 2, 3]; console.log(d.slice(0, -1)); // [1, 2] console.log(d); // [1, 2, 3]
pop() on Empty Arrays
pop() on an empty array returns undefined and is a no-op. It does not throw, which is a deliberate specification behavior. Code relying on pop() to signal emptiness should check the returned value or the array length:
const empty = []; const value = empty.pop(); if (value === undefined && empty.length === 0) { console.log("Array was empty"); }
Caution: if the array legitimately contains undefined values, undefined from pop() is ambiguous.
const values = [1, undefined]; console.log(values.pop()); // undefined console.log(values.length); // 1 — the array still had content console.log(values); // [1]
pop() and Typed Arrays
Int8Array, Uint8Array, and other typed arrays do not have pop() in their prototypes. Calling Array.prototype.pop on them throws a TypeError because their length is not writable and their indexed properties are not deletable. Use subarray or a manual approach instead:
const typed = new Uint8Array([1, 2, 3]); // Array.prototype.pop.call(typed); // TypeError const trimmed = typed.subarray(0, typed.length - 1); console.log(trimmed); // Uint8Array [1, 2]
Engine-Level Considerations
The ECMAScript specification does not mandate a specific internal representation or complexity bound for pop(). V8, SpiderMonkey, and JavaScriptCore all optimize dense array pops aggressively, often inline as the inverse of push. However, the following factors can degrade performance or alter behavior:
-
Dictionary-mode arrays: Sparse arrays or arrays with unusual property access may transition to hash-table representations where
pop()is still correct but less efficient. -
Mixed element types: The transition between internal representations may cost more for
pop()than forpush()in some microbenchmarks. -
Frozen/sealed objects: Behavior is governed by
Object.freeze/Object.sealand strict mode; engine optimizations are irrelevant when the object is non-extensible.
// Dense array: optimized fast path const dense = [1, 2, 3, 4, 5]; dense.pop(); // Dictionary-mode candidate: large sparse array const sparse = []; sparse[1000000] = "far"; sparse.pop(); // Correct, but engine may not use fast-path representation
