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.
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
| Operation | Time | Space |
|---|---|---|
| push | O(1) | O(1) |
| pop | O(1) | O(1) |
| peek | O(1) | O(1) |
| search | O(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 →