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.
1. Primitives: Pass-by-Value
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"JavaScript2. Reference Types: Pass-by-Reference
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)JavaScriptThe “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.JavaScript3. The Equality Trap (===)
This distinction is critical when comparing values.
- Primitives compare Values. (Is
10the same as10? Yes.) - References compare Memory Addresses. (Is this address
0x001the same as0x002? 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)JavaScript4. Engineering Use Cases: The “Backup” Bug
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'}JavaScriptThe 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'}JavaScriptWarning: 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: NewJavaScriptSummary Reference
| Feature | The Stack (Primitives) | The Heap (Reference Types) |
| Analogy | Fast Lockers | Open Warehouse |
| Speed | Extremely Fast | Slower |
| Size | Small / Limited | Huge / Flexible |
| Storage | Stores the Value | Stores the Data (Stack holds the address) |
| Default Copying | Creates a real copy | Copies the pointer (shared address) |
| Copying Safely | Just assign it ( = ) | Use structuredClone() for a Deep Clone |
| Comparison | Checks Value | Checks 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.




