Suggest an editImprove this articleRefine the answer for “Difference between primitives and non-primitives in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Primitives** are immutable values (`string`, `number`, `boolean`, `null`, `undefined`, `bigint`, `symbol`) stored and copied by value. **Non-primitives** (objects, arrays, functions) are mutable and copied by reference. ```javascript let a = 5; let b = a; a = 10; console.log(b); // 5 — own copy let x = {}; let y = x; console.log(x === y); // true — same reference ``` **Key:** assignment copies the value for primitives, but only the pointer for objects.Shown above the full answer for quick recall.Answer (EN)Image**Primitives** are immutable values stored directly in memory by value; **non-primitives** (objects) are mutable structures accessed through a reference to heap memory. ## Theory ### TL;DR - A primitive is like a printed postcard: copying it gives you a fully independent duplicate, and changing yours doesn't touch the original - An object is like a shared Google Doc link: everyone holding that link edits the same document - Assignment with a primitive copies the value; assignment with an object copies the reference (a pointer) - Use primitives for simple, stable data (age, ID, flag); use objects for grouped or changing data (cart, config, user profile) - `===` on primitives compares values; `===` on objects checks whether they're the same reference in memory ### Quick example ```javascript // Primitives: assignment copies the value let a = 5; let b = a; // b gets its own copy of 5 a = 10; console.log(a); // 10 console.log(b); // 5 — not affected // Objects: assignment copies the reference let x = { count: 5 }; let y = x; // y points to the same object as x x.count = 10; console.log(x.count); // 10 console.log(y.count); // 10 — same object, same result ``` Both `x` and `y` point to one object in memory. There is no second object. So mutating through `x` is visible through `y`. ### Key difference When you write `let b = a` with a primitive, JavaScript creates a new memory slot and copies the actual value into it. When you write `let y = x` with an object, JavaScript copies only the address of the object (a 64-bit pointer in V8). Both variables point to the same heap location. Any mutation through one variable shows up in the other. ### When to use - Age, price, toggle flag, user ID -> primitive - Shopping cart, user profile, app config -> object - Unique key that should not accidentally equal anything else -> `Symbol` (primitive) - Grouped data with methods -> object ### Comparison table | Aspect | Primitives | Non-primitives (Objects) | |---|---|---| | Storage | Stack (direct value) | Heap (data) + stack (reference) | | Assignment | Copies value | Copies reference | | Mutability | Immutable | Mutable | | `===` comparison | Checks value | Checks reference | | Size | Fixed, small | Dynamic | | Types | `string`, `number`, `boolean`, `null`, `undefined`, `bigint`, `symbol` | `{}`, `[]`, `() => {}` | | When to use | Constants, IDs, calculations | Data structures, configs, state | ### How it works internally V8 (Chrome and Node.js) allocates primitives on the stack for fast access with no garbage collection needed. Objects go on the heap; the stack holds only a 64-bit pointer to that heap location. When you do `obj.prop = 1`, V8 follows the pointer and mutates the shared heap data. When no references point to an object anymore, the garbage collector frees it. One detail worth knowing: V8 interns short identical strings, meaning they can share a single memory slot. That is a performance optimization you do not control, but it is why identical string primitives are always `===` equal. ### Common mistakes **Mistake 1: assuming arrays copy on assignment** ```javascript let arr1 = [1, 2]; let arr2 = arr1; // copies reference, not elements arr2.push(3); console.log(arr1); // [1, 2, 3] — surprise // Fix: use spread let arr2 = [...arr1]; ``` **Mistake 2: trying to mutate a string** ```javascript let str = 'hello'; str[0] = 'H'; console.log(str); // 'hello' — strings are immutable, assignment has no effect // Fix: build a new string str = 'H' + str.slice(1); // 'Hello' ``` **Mistake 3: comparing objects with `===`** ```javascript console.log({ a: 1 } === { a: 1 }); // false — two different references // Fix: compare by content JSON.stringify(obj1) === JSON.stringify(obj2); // For nested structures: lodash isEqual ``` **Mistake 4: forgetting `null` is a primitive** ```javascript let obj = null; obj.prop = 'test'; // TypeError: Cannot set properties of null // Fix: guard before accessing if (obj) obj.prop = 'test'; ``` ### Real-world usage - React: state is an object (`useState({ name: 'Alice' })`); update it by creating a new object (`{ ...user, name: 'Bob' }`) because mutating directly bypasses React's change detection - Express: `req.body` is a mutable object; `res.status(200)` takes a primitive number - Node.js: `process.env` is an object shared across the app; `parseInt(process.env.PORT)` gives a primitive you can pass around safely - Lodash `cloneDeep`: when you need a fully independent copy of a nested object, not just a pointer copy In practice, the object reference model causes bugs more often than the value model. Most "why did my state change unexpectedly?" questions in React trace back to shared references. ### Follow-up questions **Q:** What happens when you do `let x = 5; x = { value: 5 };`? **A:** `x` changes from holding a primitive number to holding a reference to a new object. JavaScript does not enforce types on variables, so the reassignment just works. The number `5` is gone. **Q:** Why does `typeof null` return `'object'`? **A:** It is a bug from 1995 that was never fixed to preserve backward compatibility. `null` is a primitive. The `typeof` result is wrong, but changing it would break too much existing code. **Q:** Can primitives have methods like `.toUpperCase()` or `.toString()`? **A:** Yes, through auto-boxing. When you call `'hello'.toUpperCase()`, JavaScript temporarily wraps the string primitive in a `String` object, calls the method, then discards the wrapper. The original primitive stays unchanged. **Q:** If you pass an array into a function and push an element inside, does the original array change? **A:** Yes. Arrays are objects, so passing one passes the reference. Any mutation inside the function affects the original. To avoid this, pass a copy: `fn([...arr])`. **Q:** (Senior) Two objects with identical properties give `===` false. What are the ways to check structural equality? **A:** `JSON.stringify(a) === JSON.stringify(b)` works for flat objects with consistent key order. For nested structures or objects with `Date` or `RegExp`, use lodash `isEqual`, which handles edge cases correctly. ## Examples ### Primitive: value copy inside a function ```javascript function increment(n) { n += 1; return n; } let count = 5; let result = increment(count); console.log(count); // 5 — original unchanged console.log(result); // 6 ``` The function receives a copy of `5`. Changing `n` inside does nothing to `count` outside. This is the value model in action. ### Object reference: React state mutation bug ```javascript // BAD: mutates the existing state object const [user, setUser] = useState({ name: 'Alice', prefs: { theme: 'dark' } }); user.prefs.theme = 'light'; // direct mutation setUser(user); // React sees the same reference — no re-render // GOOD: create a new object at every level that changed setUser({ ...user, prefs: { ...user.prefs, theme: 'light' } }); // React sees a new reference, triggers re-render correctly ``` `setUser(user)` passes the same pointer. React's shallow compare sees an identical reference and skips the re-render. The fix is always to return a new object, because React relies on reference inequality to detect changes.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.