DEV Community

Cover image for Primitive vs Non-Primitive Data Types in JavaScript
Arvin Jafari
Arvin Jafari

Posted on Edited on

Primitive vs Non-Primitive Data Types in JavaScript

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:

  • String
  • Number
  • BigInt
  • Boolean
  • Undefined
  • Null
  • Symbol

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";
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

What happened here?

Initially:

a → 10
b → 10
Enter fullscreen mode Exit fullscreen mode

After:

b = 20;
Enter fullscreen mode Exit fullscreen mode

we have:

a → 10
b → 20
Enter fullscreen mode Exit fullscreen mode

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:

  • Object
  • Array
  • Function

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
Enter fullscreen mode Exit fullscreen mode

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 ──┘
Enter fullscreen mode Exit fullscreen mode

After:

user2.name = "Ali";
Enter fullscreen mode Exit fullscreen mode

the same Object has changed:

user1 ──┐
        ├──→ { name: "Ali" }
user2 ──┘
Enter fullscreen mode Exit fullscreen mode

That's why:

console.log(user1.name);
Enter fullscreen mode Exit fullscreen mode

returns:

Ali
Enter fullscreen mode Exit fullscreen mode

🧠 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
};
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

a remains:

10
Enter fullscreen mode Exit fullscreen mode

because b received the value.

Non-Primitive

const user1 = {
  name: "Arvin"
};

const user2 = user1;

user2.name = "Ali";
Enter fullscreen mode Exit fullscreen mode

Now:

console.log(user1.name); // Ali
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

You might expect:

original.name === "Arvin"
Enter fullscreen mode Exit fullscreen mode

But the result is:

original.name === "Ali"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Assign it:

A → 10

B = A

A → 10
B → 10
Enter fullscreen mode Exit fullscreen mode

They have independent values.

Non-Primitive

Variable
   ↓
Reference
   ↓
Object
Enter fullscreen mode Exit fullscreen mode

Assign it:

A ──┐
    ├──→ Object
B ──┘
Enter fullscreen mode Exit fullscreen mode

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:

  1. Primitive types represent simple values.
  2. Primitive values are immutable.
  3. Primitive assignment gives another variable the value.
  4. Objects, Arrays, and Functions are mutable.
  5. Assigning a Non-Primitive value can make multiple variables refer to the same Object.
  6. 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

Infographic comparing JavaScript primitive and non-primitive data types, showing value and reference behavior

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)