Simple, single values. Stored directly in memory.
let name = "Ayushi"; // string let age = 22; // number let isOn = true; // boolean let x; // undefined let empty = null; // null
Complex values. Stored as a reference (pointer) in memory.
let user = { name: "Ayushi", age: 22 }; let nums = [1, 2, 3]; let greet = function() {};
Primitives → stored by VALUE
The actual value sits directly in the variable's memory slot.
Objects → stored by REFERENCE
The variable stores an address pointing to where the object lives.
See exactly how JS stores primitives vs objects in memory.
Objects → stored on the Heap. Stack holds only a reference (memory address) to the Heap location.
When you copy a primitive, you get a completely new independent copy. Changing one does NOT affect the other.
let a = 10; let b = a; // b gets a COPY of the value 10 b = 99; // only b changes console.log(a); // 10 ← untouched! console.log(b); // 99
When you copy an object, you copy the reference (address), not the data. Both variables point to the SAME object in memory!
let user1 = { name: "Ayushi" }; let user2 = user1; // user2 points to SAME object user2.name = "Priya"; // modifies the shared object console.log(user1.name); // "Priya" ← user1 changed too! 😱 console.log(user2.name); // "Priya"
Use spread operator or Object.assign() for shallow copy, or JSON.parse(JSON.stringify()) for deep copy.
// Shallow copy (1 level deep) let original = { name: "Ayushi", age: 22 }; let copy = { ...original }; // spread operator copy.name = "Priya"; console.log(original.name); // "Ayushi" ← safe now ✅ // Deep copy (nested objects too) let deepCopy = JSON.parse(JSON.stringify(original));
This is a very common interview trap. const just means the reference can't change — but the object's contents CAN be mutated.
const user = { name: "Ayushi" }; // ✅ This WORKS — mutating the object user.name = "Priya"; user.age = 22; // ❌ This FAILS — reassigning the variable user = { name: "Someone else" }; // TypeError!
// Primitives — compared by VALUE 5 === 5 // true ✅ "hi" === "hi" // true ✅ // Objects — compared by REFERENCE {} === {} // false ❌ (different objects in memory!) [] === [] // false ❌ const a = [1,2,3]; const b = a; a === b // true ✅ (same reference)
You can't change a primitive value — you can only replace it with a new one.
let str = "hello"; str[0] = "H"; // silently fails! console.log(str); // still "hello" // To "change" it, replace the whole value: str = "Hello"; // new string created, old one discarded
Type any JS expression in the box to see what typeof returns.
typeof "Ayushi" // "string" typeof 42 // "number" typeof true // "boolean" typeof undefined // "undefined" typeof Symbol() // "symbol" typeof 9007n // "bigint" // ⚠️ TRICK QUESTIONS — common in interviews! typeof null // "object" ← BUG in JS! null is NOT an object typeof [] // "object" ← arrays are objects typeof {} // "object" typeof function(){} // "function" ← special case
Since typeof [] returns "object", you need a different approach:
const nums = [1, 2, 3]; Array.isArray(nums); // true ✅ (best way) nums instanceof Array; // true ✅ typeof nums === "object"; // true ❌ (can't tell it's array) // Check for null specifically: const val = null; val === null; // true ✅ (only way to check null)