Skip to main content

Primitives and objects in memory

JavaScript keeps data in two places in memory: the stack holds primitives, while the heap holds objects and the stack only holds a reference to them. That is exactly why a primitive is copied by value and an object is copied by reference.

Theory

TL;DR

  • The stack is fast memory for primitives and for references to objects.
  • The heap is flexible memory where the objects themselves live: objects, arrays, functions, Date, Map, Set.
  • A primitive is copied by value: the new variable owns its own copy of the data.
  • An object is copied by reference: both variables look at the same region of the heap.
  • Spread breaks the reference only at the top level; nested structures need a deep copy (structuredClone()).

Quick example

javascript
// primitive: the value is copied let a = 10; let b = a; b = 20; console.log(a, b); // 10 20 // object: the reference is copied const user1 = { name: 'Tim' }; const user2 = user1; user2.name = 'Alex'; console.log(user1.name, user2.name); // "Alex" "Alex"

Primitives: the stack and copying by value

Primitives are string, number, boolean, null, undefined, symbol, bigint. They are stored on the stack, a fast region of memory that holds the values themselves rather than references.

javascript
let a = 10; let b = a; // the value 10 is copied b = 20; console.log(a); // 10 console.log(b); // 20

In memory (stack):

text
+---------------+ | a = 10 | | b = 20 | <- a separate copy +---------------+

Every variable stores its own value, so a change in one does not affect the other.

Objects: the heap and copying by reference

Objects, and with them arrays, functions, dates and other complex structures, are stored on the heap, the memory area for "flexible" structures. The stack holds only a reference (a pointer) to that object.

javascript
const user1 = { name: 'Tim' }; const user2 = user1; // the reference (the pointer) is copied user2.name = 'Alex'; console.log(user1.name); // "Alex"

In memory:

text
STACK HEAP +---------------+ +----------------------+ | user1 --+ | | { name: "Alex" } | | user2 --+---------> +----------------------+ +---------------+

Both variables point at one and the same object, so a change made through user2 shows up in user1.

A shallow copy and why it does not save nested objects

Dedicated copying techniques create a new reference:

javascript
const arr1 = [1, 2, 3]; const arr2 = [...arr1]; // the spread operator arr2.push(4); console.log(arr1); // [1, 2, 3] console.log(arr2); // [1, 2, 3, 4]

In memory:

text
arr1 --> [1, 2, 3] arr2 --> [1, 2, 3, 4]

Different references, so the changes do not touch the original.

But when the object is nested, spread makes only a shallow copy:

javascript
const user1 = { name: 'Tim', address: { city: 'Kyiv' } }; const user2 = { ...user1 }; user2.address.city = 'London'; console.log(user1.address.city); // "London"

Why that happens:

text
user1.address --+ user2.address --+--> they point at the same inner object

In other words, the inner objects are still copied by reference.

Deep copying

To create a fully independent copy you have to break every reference inside.

The old way:

javascript
const user1 = { name: 'Tim', address: { city: 'Kyiv' } }; const user2 = JSON.parse(JSON.stringify(user1)); user2.address.city = 'Berlin'; console.log(user1.address.city); // "Kyiv"

The modern way (Node.js 17+ and browsers from roughly 2022):

javascript
const user2 = structuredClone(user1);

structuredClone() produces a deep copy and leaves no shared references at all.

Stack vs heap and the summary table

text
STACK (fast memory) ------------------------ a = 5 b = "Hello" user -> (a reference to an object) HEAP (flexible memory) ------------------------ { name: "Tim", age: 25 }

The stack stores simple values and references, while the heap stores the complex objects themselves.

An everyday analogy:

  • A primitive is like a sheet of paper with a number on it: make a copy and you get another sheet, and changing the number on it leaves the original intact.
  • An object is like a folder of documents: if you hand a colleague a link to the same folder, you both see the same files.
Data typeWhere it is storedHow it is copiedWhat is passed
Primitives (string, number, boolean, null, undefined, symbol, bigint)StackBy valueThe value itself
Objects (object, array, function, Date, Map, Set)HeapBy referenceA pointer to the object

In short:

  • Primitives are copied by value, a new copy is created.
  • Objects are copied by reference, both variables "look at" the same region of memory.
  • For an independent copy of an object use structuredClone(), JSON.parse(JSON.stringify(obj)) or lodash.cloneDeep().

Common mistakes

  • Assuming [...arr] or { ...obj } gives a full copy. They copy only the top level.
  • Believing const makes an object immutable. const pins the reference, not the contents of the heap.
  • Applying JSON.parse(JSON.stringify(obj)) to anything at all: it drops undefined, functions and Symbol, turns a Date into a string and throws on circular references.
  • Forgetting that strings in JavaScript are immutable: str[0] = 'X' changes nothing, because a primitive cannot be edited in place.
  • Expecting a large object to be freed the moment you assign null to one of the variables. While at least one live reference remains, the garbage collector will not reclaim it.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.