# 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?

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1769070439143/2e90dcaf-d613-4af7-a38c-c039d300d9ac.png align="center")

# 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.

```javascript
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.

```javascript
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.

```javascript
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.

```javascript
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.

```javascript
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:

```javascript
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)**

```javascript
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())
```

```javascript
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.
