JavaScript Primitive vs. Reference Values

Home » JavaScript Tutorial » JavaScript Control Flow and Collections » JavaScript Primitive vs. Reference Values

In JavaScript, not all variables are created equal. The way the language stores data in memory depends entirely on the Type of data.

  • Primitives (Value Types): Simple data (string, number, boolean). Stored directly in the variable.
  • Reference Types: Complex data (Object, Array). The variable stores a Pointer (address) to the data, not the data itself.

There are 7 primitive types: string, number, boolean, null, undefined, Symbol, and BigInt.

Think of these like files on your computer. When you copy a primitive, you are creating a genuine photocopy. If you scribble on the copy, the original remains untouched.

// Primitives: Pass-by-Value
let original = "original value";
let copy = original; // copy "original value"
copy = "new value"; // Changing copy to "new value"

console.log(original); // Output: "original value"
console.log(copy); // Output: "new value"
JavaScript

Objects and Arrays are “Reference Types.” They are too big and flexible to fit in the fast memory, so JavaScript treats them differently.

To understand why, we need to look at the JavaScript Engine (the computer program that executes your code). The engine has a memory system used to remember your variables so you can refer to them later, and it organizes this storage into two specific sections: The Stack and The Heap.

The Stack: The “Fast Locker”

  • What stays here: Primitive values (numbers, strings, booleans).
  • How it works: Think of this like a set of small lockers right by the front door. It is extremely fast and organized, but it only has room for small, fixed-size items.

The Heap: The “Open Warehouse”

  • What stays here: Objects and Arrays.
  • How it works: This is the massive open floor space in the back. Because objects can grow (you can add properties later), they need plenty of space. They are placed anywhere there is a gap on the floor.

The Connection (The Reference) Since the warehouse floor is messy, JavaScript puts a “Reference” (a small note with the floor address) inside a Stack locker so it knows where to find your object or array later.

When you copy an Object variable, you are just photocopying the note (the address). You now have two notes that point to the exact same item on the warehouse floor. If you follow the second note and rearrange the furniture, it changes for the first note holder too.

// Objects & Arrays: Pass-by-Reference
const user1 = { fullName: "Original Name" };
const user2 = user1; // Copying the Reference (The Address Note), not the object.

user2.fullName = "New Name"; // Modifying the data via the second "user2" variable.
console.log(user1.fullName); // Output: "New Name" (The original changed)
JavaScript

The “Const” Myth

This explains why you can modify a const object or array. const only protects the variable binding (the note in the Stack “locker”). It prevents you from swapping the note for a different one, but it does not stop you from walking over to the Heap “warehouse” and messing up the furniture inside the object or array.

// Const Objects & Arrays can be modified:
const arrList = [1, 2];
arrList.push(3, 4); // Allowed: We are modifying the contents (Heap)
console.log(arrList); // Output : [1, 2, 3, 4]

const myObj = { id: 2, firstName: "Rodney", Age: 29 };
myObj.lastName = "Hen";
console.log(myObj);
// Output: {id: 2, firstName: 'Rodney', Age: 29, lastName: 'Hen'}

// Remember to create a copy of an object or array and that there is a difference between shallow copying and deep cloning.
JavaScript

This distinction is critical when comparing values.

  • Primitives compare Values. (Is 10 the same as 10? Yes.)
  • References compare Memory Addresses. (Is this address 0x001 the same as 0x002? No.)
// Primitives
console.log(10 === 10); // Output: true
console.log("hello" === "hello"); // Output: true

// References
console.log([1, 2] === [1, 2]); // Output: false (Two different arrays in memory)
console.log({} === {}); // Output: false (Two different objects)

const a = {};
const b = a; 
console.log(a === b); // true (They point to the same address in memory)
JavaScript

A common mistake in software engineering is trying to create a “backup” or “original state” variable before modifying data, only to realize later that the backup was corrupted.

The Bug (Copying by Reference): You try to upgrade a user to Admin, but you accidentally upgrade the original user record too.

// Mistake: Creating a reference, not a copy:
const originalUser = { name: "Guest", role: "Basic" };
const newUser = originalUser; 
newUser.role = "New"; // Changing { role: "New" }
// Original object was modified:
console.log(originalUser); // Output: {name: 'Guest', role: 'New'}
JavaScript

The Fix (Shallow Copy): Use the Spread Syntax ... (which we learned in the Array/Object lessons) to create a real copy.

// Fix: ...Spread syntax creates a new object in memory:
const originalUser = { name: "Guest", role: "Basic" };
const newUser = { ...originalUser };
newUser.role = "New";
console.log(originalUser);
// Output: {name: 'Guest', role: 'Basic'} (Original Safe)
console.log(newUser);
// Output: {name: 'Guest', role: 'New'}
JavaScript

Warning: Nested Objects (Deep Cloning)

The spread operator ... only creates a Shallow Copy. If your object has another object inside it (nested data), the copy will still point to the same inner object.

To create a Deep Clone that disconnects everything, use structuredClone().

// Fix: structuredClone() creates a Deep Clone object in memory:
const originalUser = { name: "Guest", role: {
	first: "Basic",
	second: "Pro" }
};
const newUser = structuredClone(originalUser); // DEEP CLONE
newUser.role.first = "New";
console.log(originalUser.role.first); // Output: Basic
console.log(newUser.role.first); // Output: New
JavaScript
FeatureThe Stack (Primitives)The Heap (Reference Types)
AnalogyFast LockersOpen Warehouse
SpeedExtremely FastSlower
SizeSmall / LimitedHuge / Flexible
StorageStores the ValueStores the Data (Stack holds the address)
Default CopyingCreates a real copyCopies the pointer (shared address)
Copying SafelyJust assign it ( = )Use structuredClone() for a Deep Clone
ComparisonChecks ValueChecks Memory Address

Why This Matters: In modern frameworks like React, UI updates depend on detecting changes. If you mutate an object directly (pass-by-reference), React won’t see that the memory address changed, and it may fail to re-render your screen. Always copy objects before changing them.