Primitive vs Reference Types
TL;DR
- Primitive Types (String, Number, Boolean, Null, Undefined, Symbol, BigInt): Passed and compared by Value. They are immutable.
- Reference Types (Objects, Arrays, Functions): Passed and compared by Reference (Memory Address). They are mutable.
Mental Model
How It Works
When you assign a primitive value to a variable, the variable actually holds that specific data. If you pass it to a function or assign it to another variable, JavaScript creates a strict copy of that data.
When you create an Object or Array, it is too large to fit in the standard variable space (the Stack). It is stored in the Heap memory. The variable only holds a pointer (the memory address) to that location. If you assign that variable to another variable, you are only copying the pointer, not the actual object. Both variables now look at the exact same object.
Example
// --- Primitives (By Value) ---
let score1 = 50;
let score2 = score1; // A true copy is made
score2 = 100;
console.log(score1); // 50 (Unaffected)
console.log(score2); // 100
// --- References (By Pointer) ---
let user1 = { name: "Alice" };
let user2 = user1; // Only the memory address is copied!
user2.name = "Bob"; // We mutate the shared object
console.log(user1.name); // "Bob" (Affected!)
console.log(user2.name); // "Bob"
Common Output Question
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];
console.log(arr1 === arr2);
Q: What does this output and why?
A: false.
Even though the arrays contain the exact same numbers, they are Reference Types. The === operator compares their memory addresses, not their contents. Because they were created separately ([...]), they live at two different locations in memory.
Senior Interview Question
Q: Explain how JavaScript handles passing Reference Types to function arguments. Is it truly “Pass by Reference” like in C++?
A: Technically, JavaScript is strictly “Pass by Value”. However, when dealing with objects, the value that is being passed is the memory address (the pointer).
This causes a hybrid behavior called “Call by Sharing”.
If you pass an object into a function and mutate its properties (obj.name = "Bob"), the original object is affected because you followed the pointer.
BUT, if you reassign the entire argument to a new object (obj = { name: "Bob" }), the original object is unaffected! You merely overwrote the local copy of the pointer inside the function, severing the link to the original object.