cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Memory: primitives vs objects

Build an honest mental model for JavaScript memory: stack frames, heap objects, references, reachability, and garbage collection basics.

What you’ll be able to explain
  • 01
    Use the model carefullyDescribe stack frames and heap objects without pretending to inspect engine memory.
  • 02
    Predict aliasingTell when a copy is independent and when two names share one object.
  • 03
    Reason about lifetimeUse reachability to spot leaks and understand why cycles can still be collected.

Where values live, and why references matter

You already met Data types: primitives such as numbers, strings, booleans, null, and undefined, plus objects such as arrays, functions, and object literals. You also met Objects & references: copying an object variable copies a reference, so aliases can see the same mutation. This lesson goes one level deeper: it gives you a memory model for predicting why that happens.

The curriculum summary says it plainly: where values live, and why copying an object copies a reference. We will use the common stack-and-heap model because it is useful, but we will label it honestly. JavaScript engines may store small integers inline, box numbers on the heap, keep closure variables on the heap, compress pointers, or remove allocations entirely. You are learning a reasoning model, not peeking into V8 or SpiderMonkey memory.

The honest one-sentence model

In the model, a stack frame holds the current bindings, primitive values are copied as values, object variables hold references to heap objects, and the garbage collector can reclaim objects that are no longer reachable from roots.

Stack & heap mental model

STEP THROUGH
Real-life analogyToday’s desk and a storage room

Keep the notes for today’s task on your desk. Keep bigger things you need later in a storage room. A variable that “holds an object” holds the way to find that object.

In real life: A desk for today’s work
In JavaScript: The call stack: one frame per active function
In real life: A note saying a: 1
In JavaScript: A binding holding a primitive value
In real life: A storage room holding a box
In JavaScript: A heap object
In real life: A note with a box number
In JavaScript: A variable holding a reference

Where the analogy stops: Engines can store and move values differently. Use this as a reasoning model, not a measurement.

Step through the program. Watch primitive copies stay independent, then watch two bindings point at the same object. Line 6 deliberately uses let user so we can set it to null; a const binding could not be reassigned.

Step through the stack/heap model
Step 0 of 8Ready
Your turn: follow the blue line

A conceptual stack/heap replay recorded from real JavaScript values. Predict what line 7 prints, then step through each binding and reference.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
let b = a;let user = { name: "Ada" };const other = user;other.name = "Lin";user = null;console.log(other.name);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Change the name written on line 5.

Changing the value starts a fresh recorded run.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The important surprise is line 5. other.name = "Lin" does not change the reference stored in other. It follows that reference and changes the object. Since user had the same reference, user.name would see the change too until line 6 removes that one path.

Value vs reference

COMPARE

JavaScript is often described as “primitives are passed by value, objects are passed by reference.” That phrase is close, but a sharper version is: every variable stores a value; for objects, that value is a reference. Copying the variable copies whatever value is in the binding.

Mental model vs engine reality
ThingMental modelEngine reality to remember
PrimitivesThe binding directly holds values like 1, true, or null.Engines may store small integers inline, keep values in registers, or box some numbers. You cannot observe the exact storage from normal code.
Objects and arraysThe binding holds a reference to a heap object.The object may move during compaction; the reference is an abstract language value, not a raw address you can print.
ClosuresA finished stack frame is gone, but captured variables stay available.Engines keep captured state somewhere it can outlive the call, commonly heap-allocated or optimized another way.
StringsStrings behave like primitive values.Engines may share or intern string storage. JavaScript still treats strings as immutable primitive values.
Useful rule

Reassignment changes one binding. Mutation changes an object. If two bindings refer to the same object, both observe the mutation.

Function parameters copy references

STEP THROUGH

Function calls use the same rule. A parameter receives a copy of the argument value. If the argument value is a reference, the copy points at the same object. This is sometimes called call by sharing.

Mutating vs reassigning a parameter
Step 0 of 7Ready
Your turn: follow the blue line

Parameters receive copies of values. With objects, that copied value is a reference, so mutation is visible but reassignment is local.

Running in
  1. script
Next: line 7
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
function rename(person) {  person.name = "Lin";  person = { name: "Grace" };  return person.name;} const result = rename(user);console.log(user.name, result);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Line 2 mutates object #1, so the caller sees user.name become "Lin". Line 3 only reassigns the local parameter to a new object #2, so it does not replace the caller’s user. This is the pattern behind many “why did this function change my object?” bugs.

Reachability: can anyone still find the box?

INTERACTIVE
Real-life analogyA family WhatsApp group

Start with people already in the group and follow shared contacts. If you can reach someone through that chain, JavaScript must keep the object. If not, it is eligible for cleanup.

In real life: People already in the group
In JavaScript: Roots: globals, current stack bindings, and engine-known references
In real life: A contact shared in a message
In JavaScript: Object-to-object references
In real life: Nobody reachable through the group
In JavaScript: An unreachable object
In real life: Removing unused information
In JavaScript: The garbage collector

Where the analogy stops: Messages do not control when a phone cleans storage. The model only says an object is eligible for collection.

Reachability model: cut the references
References being cutPop out in the code editor (opens in a new tab)JavaScript
let globalUser = { profile: { city: "Kochi" } };let modal = { owner: globalUser };let a = { name: "A" };let b = { name: "B", friend: a };a.friend = b;modal = null;globalUser = null;
Model graphRoots can reach user and modal

Roots

  • globalUser → user
  • modal → modal

Heap objects

  • user object

    reachable

    references: profile
  • profile object

    reachable

    no outgoing references
  • modal object

    reachable

    references: user
  • cycle A

    unreachable → eligible

    references: b
  • cycle B

    unreachable → eligible

    references: a
Step 1 of 3Roots can reach user and modal

Roots are starting points such as globals and current stack bindings. Anything found by following references from them is reachable.

A conceptual mark-and-sweep model. It marks reachability, not exact garbage-collection timing.

Notice the cycle. Objects A and B point at each other, but no root points at A or B. Modern mark-and-sweep collectors handle that: they mark from roots, never reach A or B, and can collect the whole island. A pure reference-counting collector would struggle because each object keeps the other’s count above zero; that is the classic “Reference counting & cycles” problem.

Reachable or collectible?
  • window.currentUser points to an object
  • A function creates an object and returns it to the caller
  • Two objects point at each other, but no root points at either one
  • An array slot was set to null, and no other reference exists
  • A module-level Map still stores an object as a value
  • Only a WeakMap key refers to an object
Try it yourself
0 of 6 correct

Sort each scenario by whether a root can still reach the object. Eligible does not mean collected immediately.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Garbage collection basics

Garbage collection is automatic memory management. JavaScript creates objects as your program runs. The engine periodically finds objects that are not reachable and makes their memory available for reuse. Normal code cannot observe the exact moment an ordinary object is freed, and it should not depend on that timing.

Garbage collection ideas in plain words
IdeaMeaningCareful wording
RootsStarting points such as global bindings, the current call stack, and engine-managed references.If a root can reach an object, it must stay.
Mark-and-sweepMark everything reachable from roots, then sweep away unmarked objects.This explains why unreachable cycles can be collected.
Generational GCMost objects die young, so engines often collect young objects more often than old ones.This is an optimization strategy, not a rule you control from code.
EligibilityAn unreachable object may be collected.Do not say it is collected exactly when a variable becomes null.
DevTools note

Browser DevTools Memory panels can take heap snapshots and help you find leaks. Use them to compare retained objects over time. Treat this lesson’s diagrams as conceptual models; the DevTools snapshot is the tool for investigation. The later Finding memory leaks lesson goes deeper.

Where you’ll use this

This model shows up in everyday frontend work: fixing accidental mutation, choosing Map or WeakMap, clearing subscriptions, and understanding why a page gets slower after many route changes.

A cache that must be clearedPop out in the code editor (opens in a new tab)JavaScript
const seenUsers = new Map();

function rememberUser(user) {
  seenUsers.set(user.id, user);
}

function clearFinishedUsers() {
  seenUsers.clear();
}

A module-level Map is reachable as long as the module is reachable. Its entries strongly hold their values, so a cache that only grows is a leak pattern. If the cache is for temporary metadata attached to objects, a WeakMap may be better because weakly held keys do not keep objects alive by themselves.

WeakMap for object metadataPop out in the code editor (opens in a new tab)JavaScript
const metadata = new WeakMap();

function rememberElement(element, info) {
  metadata.set(element, info);
}
  • Forgotten timers or listeners can keep callbacks and captured data reachable. Clear them.
  • Growing arrays, Maps, and caches need size limits, deletion, or clearing.
  • Detached page nodes can be kept alive by ordinary JavaScript references. Drop those references.
  • Closures can capture large data. Keep captured state small or release it when the work is done.

Common misconceptions

“Primitives always live on the stack.”

That is a simplification. The language behavior is value copying; engine storage is an implementation detail.

“An object variable contains the whole object.”

The useful model is that it contains a reference. Copying the variable copies that reference.

“Setting one variable to null frees the object.”

It removes one path. The object stays if another root can still reach it, and collection timing is not observable.

“Cycles always leak.”

Unreachable cycles are collectible with mark-and-sweep. Leaks happen when something reachable keeps growing.

“WeakMap makes values disappear immediately.”

WeakMap weakens keys, not timing. You still cannot rely on exact collection moments.

Practice: predict and fix memory behavior

5 EXERCISES
Exercise 1 · Warm-upPredict the alias

Trace the code and type the exact value printed.

Starter codePop out in the code editor (opens in a new tab)JavaScript
let user = { name: "Ada" };
let other = user;
other.name = "Lin";
user = null;
console.log(other.name);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeReachability check

    In the reachability playground, after both modal and globalUser are null, what is the nested profile object: reachable or collectible?

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeExplain the cycle

      Can two objects that only reference each other be collected after all outside references are gone?

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · ChallengeFix a growing cache

        A module-level Map grows every time a user opens a profile. Name one fix.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const seenUsers = new Map();
        
        function rememberUser(user) {
          seenUsers.set(user.id, user);
        }
        
        function clearFinishedUsers() {
          seenUsers.clear();
        }

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeAvoid unexpected mutation

          Rewrite the idea so a function adds a role without changing the object it receives. What kind of value should it return?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          function label(person) {
            return { ...person, role: "admin" };
          }

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your memory model

            7 QUESTIONS
            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7Which statement best describes the stack and heap in this lesson?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              let a = 1;
              let b = a;
              b = 2;
              console.log(a);

              Choose an answer to see the explanation.

            3. Question 3 of 7What does the object alias print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const user = { name: "Ada" };
              const other = user;
              other.name = "Lin";
              console.log(user.name);

              Choose an answer to see the explanation.

            4. Question 4 of 7A function parameter receives an object. Which operation changes the caller's object?

              Choose an answer to see the explanation.

            5. Question 5 of 7What happens to two objects that only point at each other after all roots are cut?

              Choose an answer to see the explanation.

            6. Question 6 of 7Which cache is safer when data should not keep an object alive by itself?

              Choose an answer to see the explanation.

            7. Question 7 of 7Which claim about garbage collection is honest?

              Choose an answer to see the explanation.

            Key takeaways

            • The stack/heap story is a mental model, not a literal map of engine storage.
            • Primitive copies are independent values; object copies share references.
            • Mutation changes an object; reassignment changes one binding.
            • Reachability decides whether objects are still needed. Unreachable cycles can be collectible.
            • Garbage collection timing is automatic and not something normal code should observe.

            Remember the one-liner.
            A JavaScript object stays alive while a root can reach it; copying an object variable copies the path, not the object.

            Up next: Scope & closures.

            CompleteFrontend Clear concepts. Working examples.