Stacks

Push and Pop in O(1) — JavaScript Arrays as LIFO Stacks

Stacks

A stack is a last-in, first-out structure; JavaScript arrays expose push and pop natively, giving you O(1) operations without any extra library.

4 min read Level 2/5 #dsa#stack#lifo
What you'll learn
  • Explain the LIFO principle and when a stack is the right data structure
  • Use JavaScript arrays as stacks with push, pop, and peek
  • Wrap array operations in a thin Stack class for clarity

A stack follows the last-in, first-out (LIFO) principle — the most recently added item is always the first one removed. Think of a stack of plates: you add to the top and take from the top. You never pull a plate from the middle.

Stacks appear everywhere in computing: the browser’s back-button history, the function call stack, undo/redo systems, and many parsing algorithms all rely on this single simple idea.

JavaScript Arrays as Stacks

JavaScript arrays already have the two methods you need:

  • push(value) — adds an item to the end (the “top”) — O(1)
  • pop() — removes and returns the top item — O(1)
const stack = [];

stack.push(1);   // [1]
stack.push(2);   // [1, 2]
stack.push(3);   // [1, 2, 3]

console.log(stack.at(-1)); // peek: 3 (no removal)
console.log(stack.pop());  // 3  → stack is [1, 2]
console.log(stack.pop());  // 2  → stack is [1]

The at(-1) call is a handy peek — it reads the top without removing it.

Complexity at a Glance

OperationTimeSpace
pushO(1)O(1)
popO(1)O(1)
peekO(1)O(1)
searchO(n)O(1)

All core operations are constant time because arrays keep a pointer to the end. Searching requires a linear scan — stacks are not meant for random access.

A Thin Stack Class

Wrapping the array in a small class makes intent explicit and prevents accidentally using shift or unshift, which would break the LIFO contract:

class Stack {
  #items = [];

  push(value) {
    this.#items.push(value);
    return this;
  }

  pop() {
    if (this.isEmpty()) throw new Error("Stack underflow");
    return this.#items.pop();
  }

  peek() {
    if (this.isEmpty()) throw new Error("Stack is empty");
    return this.#items.at(-1);
  }

  isEmpty() {
    return this.#items.length === 0;
  }

  get size() {
    return this.#items.length;
  }
}

const s = new Stack();
s.push("a").push("b").push("c");
console.log(s.peek()); // "c"
console.log(s.pop());  // "c"
console.log(s.size);   // 2

Private fields (#items) keep the internal array hidden so callers cannot accidentally index into it.

When to Reach for a Stack

Use a stack when the problem has a most-recent-first shape: matching brackets, tracking nested state, DFS traversal, or any situation where you need to reverse a sequence. If order of arrival matters instead of recency, you want a queue — covered later in this section.

Up Next

Learn the recurring patterns that appear across nearly every stack-based interview problem.

Stack Patterns →