Primitive vs Reference
Sticky Notes vs Business Cards
LinkedIn Hook
You copy an object in JavaScript and mutate the copy — but the original mutates too.
You pass a number to a function, change it, and the outer value stays the same. You pass an array, push one item, and suddenly the outer array has a new element.
Why? Because JavaScript has exactly 7 primitive types that live on the stack, and everything else is a reference type that lives on the heap.
When you assign a primitive, you copy the value. When you assign an object, you copy the pointer — not the data.
In this lesson you'll see the 7 primitives, the stack vs heap memory model, pass-by-value vs pass-by-sharing, and the exact shallow-copy and deep-copy patterns that avoid the most common mutation bug in JS interviews.
If you've ever been caught by
obj2 = obj1silently aliasing the same object — this lesson fixes that forever.Read the full lesson -> [link]
#JavaScript #InterviewPrep #MemoryModel #StackVsHeap #Frontend #CodingInterview #WebDevelopment
What You'll Learn
- The 7 primitive types and how they differ from reference types
- How the stack and heap store values — and why that matters for copying
- Pass-by-value vs pass-by-sharing for function arguments, and how to make real independent copies
Sticky Notes vs Business Cards
Think of primitive values like sticky notes — each one holds its own value directly. When you copy a sticky note, you create a completely independent copy. Reference types are like business cards with an address on them — when you copy the card, both copies point to the same house.
The 7 Primitives
JavaScript has exactly 7 primitive types:
// 1. string
const name = "Rakibul";
// 2. number (integers and floats, both are "number")
const age = 25;
const price = 9.99;
// 3. boolean
const isActive = true;
// 4. null (intentional absence of value)
const data = null;
// 5. undefined (declared but not assigned)
let result;
console.log(result); // undefined
// 6. symbol (unique identifier -- ES2015)
const id = Symbol("id");
// 7. bigint (arbitrary-precision integers -- ES2020)
const huge = 9007199254740993n;
Reference Types
Everything that is not a primitive is an object (a reference type):
// Objects
const person = { name: "Rakibul", age: 25 };
// Arrays (special objects)
const colors = ["red", "green", "blue"];
// Functions (callable objects)
function greet() { return "Hello"; }
// Dates, RegExp, Map, Set, etc. -- all objects
const now = new Date();
const pattern = /hello/gi;
Stack vs Heap — Memory Model
Primitives are stored directly on the stack (fast, fixed-size). Reference types store a pointer on the stack that points to the actual data on the heap (dynamic, larger).
// PRIMITIVES -- stored on the stack, copied by value
let a = 10;
let b = a; // b gets its OWN copy of the value 10
b = 20;
console.log(a); // 10 -- unchanged, they're independent
// REFERENCE TYPES -- pointer on stack, data on heap, copied by reference
let obj1 = { name: "Rakibul" };
let obj2 = obj1; // obj2 gets a copy of the POINTER, not the object
obj2.name = "Karim";
console.log(obj1.name); // "Karim" -- BOTH point to the same object!
Proving Reference Behavior with Arrays
const arr1 = [1, 2, 3];
const arr2 = arr1;
arr2.push(4);
console.log(arr1); // [1, 2, 3, 4] -- same array in memory
// To create an independent copy:
const arr3 = [...arr1]; // shallow copy via spread
const arr4 = arr1.slice(); // shallow copy via slice
const arr5 = structuredClone(arr1); // deep copy (modern)
arr3.push(5);
console.log(arr1); // [1, 2, 3, 4] -- unaffected
Function Arguments — Same Rules Apply
// Primitive: pass by value
function increment(num) {
num++;
console.log("Inside:", num); // 11
}
let count = 10;
increment(count);
console.log("Outside:", count); // 10 -- unchanged
// Reference: pass by sharing (the reference is copied)
function addSkill(person) {
person.skills.push("React");
}
const dev = { name: "Rakibul", skills: ["JS"] };
addSkill(dev);
console.log(dev.skills); // ["JS", "React"] -- mutated!
Common Mistakes
- Assuming
const obj = {...}makes the object immutable —constonly locks the binding, not the contents. You can still mutate properties of aconstobject or array. - Using
JSON.parse(JSON.stringify(obj))as a universal deep clone — it silently drops functions,undefined,Symbolkeys, and breaksDate/Map/Set. PreferstructuredClone(obj)for modern code. - Expecting the spread operator or
Object.assignto clone nested objects — both do a shallow copy. Nested objects still share references with the original.
Interview Questions
Q: What are the 7 primitive types in JavaScript?
string, number, boolean, null, undefined, symbol, and bigint.
Q: What is the difference between primitive and reference types in terms of memory?
Primitives are stored directly on the stack and copied by value -- changing one copy doesn't affect the other. Reference types store a pointer on the stack that points to data on the heap -- copying the variable copies only the pointer, so both variables point to the same object.
Q: How do you create a true copy of an object?
For a shallow copy: spread operator (
{...obj}),Object.assign({}, obj), orArray.prototype.slice(). For a deep copy:structuredClone(obj)(modern) orJSON.parse(JSON.stringify(obj))(older, with limitations like losing functions andundefined).
Q: What is the difference between stack and heap memory?
The stack is fixed-size, fast, and stores primitives and pointers directly. The heap is dynamic, larger, and stores objects, arrays, and functions. Reference variables live on the stack but their data lives on the heap — which is why copying them only copies the pointer.
Q: How does copying a primitive differ from copying an object?
Copying a primitive duplicates the value, so the two variables are independent. Copying an object duplicates only the pointer, so both variables alias the same underlying object — mutating one mutates the other.
Q: What is structuredClone and when would you use it?
structuredCloneis a built-in that produces a deep copy of almost any value, including nested objects, arrays,Date,Map,Set, and cyclic references. Use it whenever you need an independent deep copy and don't want the limitations ofJSON.parse(JSON.stringify(...)).
Quick Reference — Cheat Sheet
PRIMITIVE vs REFERENCE -- QUICK MAP
7 Primitives (stored on STACK, copied by VALUE):
string | number | boolean | null | undefined | symbol | bigint
Reference types (pointer on stack, data on HEAP, copied by REFERENCE):
object | array | function | Date | RegExp | Map | Set | ...
Copy behavior:
let b = a -> primitives: independent copy
let o2 = o1 -> objects: same pointer, shared data
Make real copies:
Shallow: {...obj} | Object.assign({}, obj) | arr.slice()
Deep: structuredClone(obj) (preferred)
JSON.parse(JSON.stringify(obj)) (drops fns/undefined/symbols)
Function args:
Primitive -> pass by value (outer stays unchanged)
Reference -> pass by sharing (outer mutates if you mutate props)
Previous: Array Methods -> The Functional Toolbox Next: Type Coercion -> The Overly Helpful Translator
This is Lesson 8.1 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.