Stack Implementation in JavaScript

Learn Stack Implementation in JavaScript with practical examples, explanations, best practices, and real-world coding techniques.

A stack in JavaScript is a linear data structure that follows the LIFO (Last In, First Out) principle. The element added most recently is the first element removed.

Stacks are useful whenever data needs to be processed in reverse order. Common examples include browser history, undo and redo functionality, function call management, expression evaluation, backtracking algorithms, and depth-first search.

Think of a stack of plates. You place a new plate on the top, and when you remove a plate, you take the one from the top first.

This is the basic idea behind LIFO: the last item added is the first item removed.

How a Stack Works

A stack generally exposes a small set of core operations:

  • Push: Add an element to the top.
  • Pop: Remove and return the top element.
  • Peek: View the top element without removing it.
  • isEmpty: Check whether the stack contains elements.
  • Size: Determine the number of elements.

In a stack, insertion and deletion happen at the same end, commonly called the top.

Implementing a Stack with a JavaScript Array

JavaScript arrays provide push() and pop(), making them a straightforward way to implement a stack.

const stack = [];

stack.push("A");
stack.push("B");
stack.push("C");

console.log(stack);

const removed = stack.pop();

console.log("Removed:", removed);
console.log("Stack:", stack);

After pushing A, B, and C, the top of the stack is C. Calling pop() therefore removes C first.

Push Operation

The push() method adds one or more elements to the end of an array. When the end of the array represents the top of the stack, this becomes the stack's push operation.

const stack = [];

stack.push(10);
stack.push(20);
stack.push(30);

console.log(stack);

The stack now contains [10, 20, 30], with 30 at the top.

Pop Operation

The pop() method removes and returns the last element. This matches the LIFO behavior of a stack when the array's end is treated as the top.

const stack = [10, 20, 30];

const item = stack.pop();

console.log("Removed:", item);
console.log("Remaining:", stack);

If the stack is empty, pop() returns undefined.

Peek at the Top Element

JavaScript arrays do not have a built-in method named peek(). The top element can be inspected using the last index or the at() method.

const stack = [10, 20, 30];

const top = stack.at(-1);

console.log("Top:", top);
console.log("Stack:", stack);

The important difference is that peeking does not remove the element from the stack.

Checking Whether a Stack Is Empty

The length property can be used to determine whether an array-based stack contains any elements.

const stack = [];

if (stack.length === 0) {
  console.log("Stack is empty");
}

This check is particularly useful before accessing or removing the top element.

Stack Size

The number of elements in an array-based stack is available through the length property.

const stack = ["HTML", "CSS", "JavaScript"];

console.log("Stack size:", stack.length);

Creating a Stack Class in JavaScript

For applications where stack behavior should be encapsulated, you can create a custom JavaScript class.

class Stack {
  constructor() {
    this.items = [];
  }

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

  pop() {
    return this.items.pop();
  }

  peek() {
    return this.items.at(-1);
  }

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

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

const stack = new Stack();

stack.push(10);
stack.push(20);
stack.push(30);

console.log(stack.peek());
console.log(stack.pop());
console.log(stack.size());
console.log(stack.isEmpty());

Encapsulating the operations in a class makes the intended data structure behavior explicit and prevents application code from depending directly on the underlying array.

Stack Implementation Without a Class

A stack can also be represented with simple functions when a class is unnecessary.

const stack = [];

function push(value) {
  stack.push(value);
}

function pop() {
  return stack.pop();
}

function peek() {
  return stack.at(-1);
}

push("HTML");
push("CSS");
push("JavaScript");

console.log(peek());
console.log(pop());

Processing a Stack Until It Is Empty

A common stack pattern is to repeatedly remove the top element until no elements remain. This processes values in reverse order of insertion.

const stack = [1, 2, 3, 4, 5];

while (stack.length > 0) {
  console.log(stack.pop());
}

The output starts with 5 and ends with 1 because the most recently inserted value is removed first.

Using a Stack for Undo Operations

A stack can store previous application states or actions so that the most recent action can be undone first.

const history = [];

history.push("Typed Hello");
history.push("Added an image");
history.push("Changed the font");

const lastAction = history.pop();

console.log("Undo:", lastAction);

In a real editor, the stack could contain structured objects describing the action and the information required to reverse it.

Balanced Parentheses Using a Stack

One of the classic applications of a stack is checking whether opening and closing parentheses are correctly matched.

function isBalanced(expression) {
  const stack = [];

  for (const char of expression) {
    if (char === "(") {
      stack.push(char);
    } else if (char === ")") {
      if (stack.length === 0) {
        return false;
      }

      stack.pop();
    }
  }

  return stack.length === 0;
}

console.log(isBalanced("(a + b)"));
console.log(isBalanced("(a + b")); 
console.log(isBalanced("a + b)"));

The stack stores unmatched opening parentheses. When a closing parenthesis appears, the most recent opening parenthesis is removed.

Stack for Reversing Data

Because a stack returns elements in reverse insertion order, it can be used to reverse a sequence.

function reverseString(text) {
  const stack = [];

  for (const char of text) {
    stack.push(char);
  }

  let result = "";

  while (stack.length > 0) {
    result += stack.pop();
  }

  return result;
}

console.log(reverseString("JavaScript"));

Stack and Function Calls

JavaScript itself uses a call stack to keep track of function execution. When a function is called, its execution context is placed on the call stack. When the function finishes, it is removed.

function first() {
  second();
}

function second() {
  third();
}

function third() {
  console.log("Executing third");
}

first();

Conceptually, third() is completed first, followed by second(), and then first(). Understanding the call stack is useful when learning recursion and debugging nested function calls.

Stack for Depth-First Search

A stack can implement Depth-First Search (DFS) iteratively. Instead of relying on recursion, nodes can be explicitly stored in a stack.

const graph = {
  A: ["B", "C"],
  B: ["D"],
  C: ["E"],
  D: [],
  E: []
};

const stack = ["A"];
const visited = new Set();

while (stack.length > 0) {
  const node = stack.pop();

  if (visited.has(node)) {
    continue;
  }

  visited.add(node);
  console.log(node);

  for (const neighbor of graph[node]) {
    stack.push(neighbor);
  }
}

Stack Time Complexity

When the end of a JavaScript array is used as the top of the stack, the core operations are efficient.

Operation Typical Complexity
Push O(1) amortized
Pop O(1)
Peek O(1)
isEmpty O(1)
Size O(1)
Search O(n)

The amortized O(1) cost for push() accounts for the occasional internal resizing that may be required as an array grows.

Why Use the End of an Array as the Stack Top?

A JavaScript array can add and remove elements from both ends, but the end is generally preferable for a stack because push() and pop() are designed for efficient end operations.

Using shift() and unshift() would make the beginning of the array the stack top, but these operations may require existing elements to be shifted.

Stack vs Queue in JavaScript

Both stacks and queues are linear data structures, but they process elements in different orders.

Feature Stack Queue
Ordering LIFO FIFO
Add operation Push Enqueue
Remove operation Pop Dequeue
Common uses Undo, recursion, DFS Scheduling, BFS, processing order

Common Stack Mistakes in JavaScript

  • Using shift() and unshift() when efficient end operations are sufficient.
  • Calling pop() without considering that an empty stack returns undefined.
  • Confusing LIFO stack behavior with FIFO queue behavior.
  • Removing an element when only its value needs to be inspected.
  • Forgetting to check whether a stack is empty in algorithms that repeatedly pop values.
  • Using a stack without tracking visited nodes in graph traversal when repeated visits are possible.
  • Building large recursive solutions without considering JavaScript call stack limits.

Where Are Stacks Used?

  • Browser back and forward history.
  • Undo and redo functionality.
  • Function calls and recursion.
  • Expression parsing and evaluation.
  • Balanced parentheses and delimiter checking.
  • Depth-first search and graph traversal.
  • Backtracking algorithms.
  • Navigation through nested application states.
  • Syntax processing and compiler-related algorithms.

Practical Example: Browser History

Browser-style back navigation can be modeled using a stack. Each newly visited page is pushed onto the history, and going back removes the current page from the top.

const history = [];

function visit(page) {
  history.push(page);
}

function goBack() {
  if (history.length <= 1) {
    return "No previous page";
  }

  history.pop();
  return history.at(-1);
}

visit("home");
visit("products");
visit("products/123");
visit("checkout");

console.log(goBack());
console.log(goBack());

Real browser history is more sophisticated than this example, but the stack concept demonstrates why recently visited states can naturally be processed in reverse order.

Key Takeaways

  • A stack follows the LIFO principle: Last In, First Out.
  • The main operations are push, pop, peek, isEmpty, and size.
  • JavaScript arrays provide a simple and practical stack implementation.
  • Use push() and pop() at the array's end for efficient stack operations.
  • Peeking can be performed with array.at(-1) without removing the top element.
  • Stacks are useful for undo operations, recursion, parsing, backtracking, and DFS.
  • Most core array-based stack operations are O(1), with push being O(1) amortized.
  • Understanding stacks is important for data structures, algorithms, and JavaScript execution concepts.

Code Examples

const stack = [];

stack.push("A");
stack.push("B");
stack.push("C");

console.log("Top:", stack.at(-1));

console.log("Removed:", stack.pop());
console.log("Removed:", stack.pop());

console.log("Remaining:", stack);
    
class Stack {
  constructor() {
    this.items = [];
  }

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

  pop() {
    return this.items.pop();
  }

  peek() {
    return this.items.at(-1);
  }

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

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

const stack = new Stack();

stack.push(10);
stack.push(20);
stack.push(30);

console.log(stack.peek());
console.log(stack.pop());
console.log(stack.size());
    
function reverseString(text) {
  const stack = [];

  for (const char of text) {
    stack.push(char);
  }

  let result = "";

  while (stack.length > 0) {
    result += stack.pop();
  }

  return result;
}

console.log(reverseString("JavaScript"));
    
function isBalanced(expression) {
  const stack = [];

  for (const char of expression) {
    if (char === "(") {
      stack.push(char);
    } else if (char === ")") {
      if (stack.length === 0) {
        return false;
      }

      stack.pop();
    }
  }

  return stack.length === 0;
}

console.log(isBalanced("(a + b)"));
console.log(isBalanced("(a + b"));
console.log(isBalanced("a + b)"));
    
const graph = {
  A: ["B", "C"],
  B: ["D"],
  C: ["E"],
  D: [],
  E: []
};

const stack = ["A"];
const visited = new Set();

while (stack.length > 0) {
  const node = stack.pop();

  if (visited.has(node)) {
    continue;
  }

  visited.add(node);
  console.log(node);

  for (const neighbor of graph[node]) {
    stack.push(neighbor);
  }
}
    

Pro Tip

For an array-based stack in JavaScript, use push() and pop() at the end of the array. Avoid using shift() and unshift() for the core stack operations when performance matters because removing or inserting at the beginning can require elements to be shifted.

🎨 IgKnight Docs

Master HTML, CSS, JS, Python, .NET and more with easy-to-follow lessons and practical code examples.

© 2025 IgKnight Learning Platform. All rights reserved.