One of the most interesting and important concepts in JavaScript is the difference between Primitive and Non-Primitive data types.
At first, it may look like a simple topic. But if you don't understand how these values behave, you can run into some surprisingly strange bugs.
So, what actually happens when we assign, copy, or modify these values?
Let's break it down.
🟠 Primitive Types
JavaScript has 7 Primitive data types:
StringNumberBigIntBooleanUndefinedNullSymbol
Primitive values represent simple, basic values.
One of the most important characteristics of Primitive values is that they are immutable.
What does immutable mean?
It means that the value itself cannot be changed after it has been created.
For example:
let name = "Arvin";
name = "Ali";
This doesn't modify the original "Arvin" value.
Instead, name is now associated with a different value.
📦 Primitive Values Are Assigned by Value
Another important behavior is how Primitive values are assigned to variables.
When you assign a Primitive value to another variable, the new variable receives the value itself.
let a = 10;
let b = a;
b = 20;
console.log(a); // 10
console.log(b); // 20
What happened here?
Initially:
a → 10
b → 10
After:
b = 20;
we have:
a → 10
b → 20
The two variables have independent values.
Changing b doesn't affect a.
This is the important idea:
Primitive assignment gives the other variable its value.
🟡 Non-Primitive Types
The main Non-Primitive types discussed here are:
ObjectArrayFunction
These values are more complex than Primitive values and can contain multiple values and properties.
They are also commonly called Reference Types.
Unlike Primitive values, Objects, Arrays, and Functions are mutable.
That means their contents can be changed.
🔗 Non-Primitive Values and References
This is where things become interesting.
Consider this example:
const user1 = {
name: "Arvin"
};
const user2 = user1;
user2.name = "Ali";
console.log(user1.name); // Ali
console.log(user2.name); // Ali
You might initially expect user1 to remain unchanged.
But it doesn't.
Why?
Because user2 is not an independent copy of user1.
Both variables can access the same Object.
A simplified mental model looks like this:
user1 ──┐
├──→ { name: "Arvin" }
user2 ──┘
After:
user2.name = "Ali";
the same Object has changed:
user1 ──┐
├──→ { name: "Ali" }
user2 ──┘
That's why:
console.log(user1.name);
returns:
Ali
🧠 But What About Stack and Heap?
To understand these behaviors more easily, you will often hear about two concepts:
- Stack
- Heap
These concepts are commonly used as a mental model for understanding memory management.
🔵 Stack
A Stack is a LIFO data structure.
LIFO means:
Last In, First Out
In a simplified model, the Stack is commonly associated with managing:
- Function calls
- Execution contexts
- Data related to function execution
You can think of it as an area that helps JavaScript keep track of what is currently being executed.
🟣 Heap
The Heap is commonly described as an area used for dynamic memory allocation.
It's often used as a mental model for understanding where more complex data such as:
- Objects
- Arrays
- Functions
can be managed.
For example:
const user = {
name: "Arvin",
age: 23
};
The Object is conceptually associated with dynamically allocated memory.
⚠️ An Important Note About Stack and Heap
There is an important detail that is easy to miss.
Stack and Heap should not be treated as a literal specification of how JavaScript stores every value in memory.
They are better understood as a mental model that helps us reason about JavaScript's memory behavior.
The actual implementation depends on the:
- JavaScript Engine
- Runtime
- Memory management system
- Engine optimizations
So saying:
"All Primitive values are always stored on the Stack and all Objects are always stored on the Heap."
is an oversimplification.
The reality is more complicated.
For learning purposes, however, the Stack/Heap model can be very useful for building intuition.
🔄 Primitive vs Non-Primitive
Let's put the important differences together.
| Primitive | Non-Primitive |
|---|---|
String |
Object |
Number |
Array |
BigInt |
Function |
Boolean |
Mutable |
Undefined |
Reference-based behavior |
Null |
Can contain multiple values/properties |
Symbol |
More complex data structures |
| Immutable | Mutable |
🧩 The Most Important Difference
The easiest way to remember the concept is to focus on assignment behavior.
Primitive
let a = 10;
let b = a;
b = 20;
a remains:
10
because b received the value.
Non-Primitive
const user1 = {
name: "Arvin"
};
const user2 = user1;
user2.name = "Ali";
Now:
console.log(user1.name); // Ali
because both variables can access the same Object.
🚨 Why Does This Matter?
This difference becomes especially important when working with:
- Objects
- Arrays
- Function parameters
- State management
- API data
- React applications
- Copying data
- Updating nested objects
- Debugging unexpected changes
For example, imagine you think you've created a copy:
const original = {
name: "Arvin"
};
const copy = original;
copy.name = "Ali";
You might expect:
original.name === "Arvin"
But the result is:
original.name === "Ali"
Understanding how Non-Primitive values behave makes this kind of JavaScript behavior much easier to understand.
🎯 A Simple Mental Model
You can remember it like this:
Primitive
Variable
↓
Value
Assign it:
A → 10
B = A
A → 10
B → 10
They have independent values.
Non-Primitive
Variable
↓
Reference
↓
Object
Assign it:
A ──┐
├──→ Object
B ──┘
Both variables can access the same Object.
💡 Final Takeaway
Primitive and Non-Primitive values may look like a basic JavaScript topic, but understanding their behavior is extremely useful.
The key ideas are:
- Primitive types represent simple values.
- Primitive values are immutable.
- Primitive assignment gives another variable the value.
- Objects, Arrays, and Functions are mutable.
- Assigning a Non-Primitive value can make multiple variables refer to the same Object.
- Stack and Heap are useful mental models for understanding memory, not a complete description of how every JavaScript engine works.
Once you understand these concepts, many of JavaScript's "weird" behaviors start to make much more sense.
📌 Quick Reference
The next time an Object unexpectedly changes somewhere else in your code, one of the first things to ask yourself is:
"Are these two variables accessing the same Object?"

Top comments (0)