Understanding Stack Data Structure in JavaScript (With Real Examples)

What is a Stack?
A stack is a data structure where the last thing you put in is the first thing you take out.
Key Rule:
LIFO (Last In, First Out)
Real-Life Examples of Stack
Undo / Redo in a Text Editor
We type A B C in text editor, A, B , C all are pushed in a stack 1 ( we have to stacks one for undo one for redo, whatever we type goes in stack 1 and initially stack 2 is empty.
Then we press ctrl + Z, Now C is gone, where ? in stack 2 so that if we need to do Redo we should be able to take that value from stack 2 and put back in stack 1.
Makes sense?

A stack is ultimately a list, but with strict access rules.
Rules:
Elements can be added only from one end (top)
Elements can be removed only from that same end
No access to middle or bottom elements
Defining a Stack in JavaScript
To create a stack, we first need a container that will hold our data.
In JavaScript, the simplest and most efficient choice is an array.
So we start by defining a Stack class.
class Stack{
constructor(){
this.stack = []
}
}
What is happening here?
We create a class called Stack
Inside the constructor, we initialize an empty array
This array will store all stack elements
The end of the array represents the top of the stack
At this point, we have a stack structure, but it can’t do anything yet.
Adding Data to the Stack (push)
To add elements to a stack, we use the push operation.
class Stack{
constructor(){
this.stack = []
}
// Stack only grows from one end. This is how new data enters.
push(data){
this.stack.push(data)
}
// Mental Model: I am doing something new - save it for possible undo later.
}
What this does:
Takes data as input
Adds it to the top of the stack
Internally, Array.push() adds the element to the end of the array
This follows stack rules because:
We are adding elements only from one end
No middle or bottom insertion is allowed
Removing Data from the Stack (pop)
To remove the most recent element, we use pop.
class Stack{
constructor(){
this.stack = []
}
push(data){
this.stack.push(data)
}
pop(){
this.stack.pop()
}
// Mental Trigger: Take back the most recent thing.
}
What this does:
Removes the top element of the stack
Follows LIFO (Last In, First Out)
Uses JavaScript’s Array.pop() internally
This is how undo, backtracking, and function calls work.
Viewing the Top Element (peek)
Sometimes we only want to see the top element without removing it.
class Stack{
constructor(){
this.stack = []
}
push(data){
this.stack.push(data)
}
pop(){
this.stack.pop()
}
// Why it exist : something we neede to look before we act.
peek(){
return this.stack[this.stack.length - 1]
}
}
What this does:
Accesses the last element of the array
Returns it without modifying the stack
This is useful when:
You want to know what will be popped next
You need to validate something before removing it
Checking if the Stack Is Empty (isEmpty)
Before popping, it’s important to know whether the stack has elements or not.
class Stack{
constructor(){
this.stack = []
}
push(data){
this.stack.push(data)
}
pop(){
this.stack.pop()
}
peek(){
return this.stack[this.stack.length - 1]
}
// why it exits : popping from an empty stack = bug/ crash/ undefiend behaviour
isEmpty(){
return this.stack.length === 0
}
}
What this does:
Returns true if the stack has no elements
Returns false otherwise
This prevents:
Errors
Unexpected behavior
Crashes from popping an empty stack
Stack Utility Methods: size, clear, contains, and reverse
After implementing the core stack operations, we usually need a few helper methods to make the stack easier to work with.
These methods don’t change how a stack behaves, but they help us inspect, reset, or validate the stack.
Below is the relevant part of the stack implementation:
class Stack{
constructor(){
this.stack = []
}
push(data){
this.stack.push(data)
}
pop(){
this.stack.pop()
}
peek(){
return this.stack[this.stack.length - 1]
}
isEmpty(){
return this.stack.length === 0
}
// Returns the number of elements in the stack
size(){
return this.stack.length
}
// Clears the entire stack: Yes its that simple
clear(){
this.stack = []
}
// Checks if a value exists anywhere in the stack
contains(element){
return this.stack.includes(element)
}
// Optional: It reverses
reverse(){
this.stack.reverse()
}
}
size()
The size() method returns the total number of elements currently present in the stack.
It simply returns the length of the underlying array and does not modify the stack in any way.
This is useful for debugging, validations, or when you need to know how full the stack is.
clear()
The clear() method removes all elements from the stack.
Instead of popping elements one by one, we just assign a new empty array.
Sometimes the easiest solution really is to start fresh.
This is commonly used when:
Resetting application state
Clearing undo history
Reinitializing the stack
contains(element)
The contains() method checks whether a given element exists anywhere in the stack.
While this is useful, it’s worth noting that this method breaks pure stack abstraction, since a stack is supposed to expose only the top element.
That said, it’s perfectly fine as a utility method for learning, debugging, or validation.
Using the Stack (Final Example)
class Stack{
constructor(){
this.stack = []
}
// To add data in stack
push(data){
this.stack.push(data)
}
pop(){
this.stack.pop()
}
peek(){
return this.stack[this.stack.length - 1]
}
isEmpty(){
return this.stack.length === 0
}
size(){
return this.stack.length
}
clear(){
this.stack = []
}
contains(element){
return this.stack.includes(element)
}
reverse(){
this.stack.reverse()
}
printStack(){
let str = ""
for (let i = 0; i < this.stack.length; i++) {
str += this.stack[i] + "\n"
}
return str
}
}
const myStack = new Stack()
myStack.push(8)
myStack.push(3)
myStack.push(4)
myStack.push(3)
console.log("This is the Element at Top: ".myStack.peek())
console.log("Now printing the stack values: ")
console.log(myStack.printStack())
asmit~$node stack/index.js
This is the Element at Top: 3
Now printing the stack values:
8
3
4
3
This shows:
peek() returns the most recently added element
printStack() displays the stack from bottom to top
Stack behavior follows Last In, First Out
Final Thoughts
A stack is simple in structure but extremely powerful in practice.
It is used in:
Undo / Redo systems
Function call handling
Expression evaluation
Backtracking problems
Once you understand stacks clearly, learning Queue, Linked List, and Recursion becomes much easier.
This implementation is intentionally kept simple to focus on understanding, not overengineering.
What’s Next?
In the next article, we’ll look at the Queue data structure and see how changing just one rule completely changes behavior.
Key Takeaway
A stack is just a list with discipline.
