completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Objects & references

Learn why two variables can point to the same object, how object identity affects functions and comparisons, and when shallow or deep copies are the safer choice.

By the end, you can
  • 01
    Follow shared objectsExplain why a change made through one variable can show up through another variable.
  • 02
    Predict function calls and comparisonsTell when a function's change reaches your object, and know that === checks for the very same object.
  • 03
    Choose the right copyKnow how much spread, Object.assign, and structuredClone each copy.

One object can have many names

The Data types lesson said two things. When you copy a number or a string, you copy the value itself. When you copy an object, you copy a reference to it. This lesson shows what that means in real code.

A reference is how JavaScript finds an object. Think of it as the object's address. You never see the reference itself. You only see the object it leads to.

The main idea

A variable that holds an object doesn't hold the whole object. It holds a reference: a way to find the object. When you copy that variable, you copy the reference. You don't get a new object. So two variables can point at the same object. A change made through one name shows up through the other.

Two names for one cartPop out in the code editor (opens in a new tab)JavaScript
const cart = { item: "tea" };
const checkoutCart = cart;
checkoutCart.item = "coffee";
console.log(cart.item); // coffee

Line 1 makes one cart object with the item "tea". Line 2 copies the reference into checkoutCart. It does not make a second cart. Line 3 changes the item to "coffee" through checkoutCart. Line 4 reads the item through cart and prints coffee, because there is only one cart.

Two boxes, cart and checkoutCart, each have an arrow. Both arrows point at the same single cart, which holds a card that says item: coffee.cartcheckoutCartitem: "coffee"one object
cart and checkoutCart both point at the same object. There is only one cart. Line 3 changes its item through checkoutCart, so cart sees "coffee" too.
Real-life analogyTwo shortcuts to one shopping list

Your family keeps one shopping list on a shared app. You have a shortcut to it on your phone. Your sister adds a shortcut on her phone too. That gives you two shortcuts, but there is still only one list. If she changes an item, you see the change when you open the list.

In real life: The one shopping list
In JavaScript: The object
In real life: A shortcut to the list on a phone
In JavaScript: A reference stored in a variable
In real life: Your sister adds her own shortcut
In JavaScript: const checkoutCart = cart;
In real life: She changes an item on the list
In JavaScript: checkoutCart.item = "coffee";

This rule shows up all the time in real code:

  • arrays, which are objects too
  • settings objects passed around a program
  • objects passed to functions
  • code that must not change data by accident

Later lessons go deeper: Memory: primitives vs objects, Freezing, sealing & immutability, Immutability, and Messaging & structured cloning. This lesson covers the everyday rules.

Copying a value vs copying a reference

INTERACTIVE

Primitives are the simple values: numbers, strings, booleans, null, and undefined. When you copy a primitive, you get your own copy of the value. Changing one variable later does not change the other. Objects work differently. When you copy an object variable, you copy the reference, so both variables point at the same object.

Primitive copy vs object referencePop out in the code editor (opens in a new tab)JavaScript
let x = 1;
let y = x;
y = 2;
console.log(x); // 1

const a = { score: 1 };
const b = a;
b.score = 2;
console.log(a.score); // 2

Lines 1 to 4 copy a number. y gets its own 1, so line 3 changes only y, and line 4 prints 1. Lines 6 to 9 copy an object variable. b points at the same object as a. Line 8 changes score through b, so line 9 prints 2.

The playground below runs real code step by step. For each step it shows two lists:

  • on the left, each variable and the object it points at
  • on the right, each object, with a number like #1 so you can tell them apart

The numbers are only labels for this lesson. They are not real memory addresses.

Follow the references, step by step
Copy and change objectsPop out in the code editor (opens in a new tab)JavaScript
const a = { score: 1 };const b = a;b.score = 2;const c = { ...a };c.score = 3;a.stats = { level: "bronze" };const d = { ...a };d.stats.level = "silver";
Which object each name points atTwo names, one object
Variables
a→ #1
b→ #1
Objects
#1{ score: 1 }
Step 1 of 5Two names, one object

Line 2 copies the reference, so a and b both point at object #1.

Each step runs the real code. The #1, #2 labels only tell the objects apart; they are not real memory addresses.

Ask two questions at each step. First: do two variables point at the same object? Second: if there is a copy, does it still share an object stored inside it? Step 5 shows the second case. The section on shallow copies explains it.

Passing an object to a function

STEP THROUGH

Function parameters follow the same rule. When you pass an object to a function, the parameter gets a copy of the reference. So the parameter and your variable point at the same object. This means:

  • If the function changes a property of the object, you see that change after the call.
  • If the function gives the parameter a whole new value, like user = { ... }, only the parameter points somewhere new. Your variable does not change.

This rule is called call by sharing: the function shares your object, but not your variable. Some people call it "pass by reference". That name is not quite right. With true pass by reference, a function could make your variable point at a different object, and JavaScript can't do that.

A function changes the object you pass inPop out in the code editor (opens in a new tab)JavaScript
function markDone(todo) {
  todo.done = true;
}

const todo = { done: false };
markDone(todo);
console.log(todo.done); // true

Lines 1 to 3 make a function, markDone. Line 2 sets the done property of the object it gets. Line 5 makes a todo object with done: false. Line 6 passes it to the function, so the parameter points at the same object. Line 7 prints true, because the function changed that one object.

In the replay below, you can switch the function body. Compare the two versions:

  • Change a property: line 2 adds badge to the shared object, so ada.badge is "mentor".
  • Give the parameter a new object: line 2 points user at a different object, so ada.badge stays undefined.
Change a property, or give the parameter a new object?
Step 0 of 4Ready
Your turn: follow the blue line

Switch the function body. Changing a property reaches ada's object. Giving user a new object does not.

Running in
  1. script
Next: line 5
function addBadge(user) {  user.badge = "mentor";} addBadge(ada);console.log(ada.badge);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Switch the function body

Changing the body starts the replay again from the first step.

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.
A box called ada points at a cart that holds name: Ada. A second box, the parameter user, points at a different, new cart that holds name: Grace.adaname: "Ada"username: "Grace"
At first, user pointed at the same object as ada. Then user = { ... } pointed user at a new object. ada did not move, so ada.badge is still undefined.
Real-life analogySharing your list with a helper

You give a helper a shortcut to your shopping list. If the helper changes an item, you see it, because it is your list. If the helper starts a brand-new list of their own, your list stays the same.

In real life: You give a helper a shortcut to your list
In JavaScript: Passing an object to a function
In real life: The helper changes an item on your list
In JavaScript: The function changes a property
In real life: The helper starts a new list of their own
In JavaScript: The function gives the parameter a new object

Comparing objects

STEP THROUGH

The Comparisons lesson taught ===, strict equality. For objects, === asks one question: "Do both sides point at the very same object?" This is called checking identity. It does not look at the keys and values inside.

Same contents, but two different objectsPop out in the code editor (opens in a new tab)JavaScript
const first = { score: 1 };
const second = { score: 1 };
console.log(first === second); // false

Line 1 makes an object with score: 1. Line 2 makes another object with score: 1. Each pair of curly braces { } makes a brand-new object. So these are two objects that look alike. Line 3 prints false, because they are not the same object.

Two separate carts, first and second. Each holds a card that says score: 1. Above them, first === second. Below them, the answer false.first === secondscore: 1firstscore: 1secondfalse
The two carts hold the same thing, but they are two different objects. === asks "is this the very same object?", so the answer is false.
=== checks for the same object, not the same contents
Step 0 of 6Ready
Your turn: follow the blue line

Guess each result first. For objects, === asks: are both sides the very same object?

Running in
  1. script
Next: line 1
const b = a;const c = { score: 1 }; console.log(a === b);console.log(a === c);console.log({ score: 1 } === { score: 1 });
CallStoreChangeResultBlue line: runs next. 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.

Sometimes you do want to know if two objects hold the same things. The table shows three ways to compare. Read each row as: "to answer this question, write this, and watch out for this."

Three ways to compare two objects
What you want to knowWhat to writeWatch out
Is it the very same object?a === bIt is true only when both sides point at one object.
Do they hold the same keys and values, one level deep?Compare the keys, then each value with Object.isIt only checks the top level. Objects stored inside need their own check.
Quick check: do they turn into the same text?JSON.stringify(a) === JSON.stringify(b)Keys in a different order give different text. The text also leaves out undefined and functions.

For a flat object, one with no objects stored inside it, you can compare contents yourself. Check that both objects have the same keys, and that each key has the same value. You have to write that check. === never does it for you.

Shallow copies with spread and Object.assign

INTERACTIVE

A shallow copy makes a new outer object and copies each top-level property into it. If a property holds another object, only the reference to that inner object is copied. There are three common ways to make one:

  • { ...obj }, called spread, for objects
  • Object.assign(, obj), which copies the properties of obj into a new empty object
  • [...array], spread for arrays
A shallow copy shares the object inside itPop out in the code editor (opens in a new tab)JavaScript
const user = {  name: "Asha",  delivery: { note: "Leave at door" },}; const shallow = { ...user };shallow.delivery.note = "Ring bell"; const deep = structuredClone(user);deep.delivery.note = "Call first"; console.log(user.delivery.note);console.log(deep.delivery.note);

Lines 1 to 4 make a user object. Its delivery property holds a second object, with a note. Line 6 makes shallow, a new outer object. But shallow.delivery and user.delivery point at the same inner object. Line 7 changes the note through shallow, so user sees the new note too. Line 12 prints Ring bell.

Two carts, user and shallow. Each holds a card that says delivery. An arrow from each delivery card points up to the same single card, note: Ring bell.note: "Ring bell"shared by bothdeliveryuserdeliveryshallow
{ ...user } makes a new outer cart, shallow. But its delivery is the same inner object as user.delivery. So changing the note through shallow changes it for user too.

Lines 9 and 10 use structuredClone, which copies the inner object too. The next section explains it. Line 13 prints Call first, and the note in user stays Ring bell.

Spread makes a new arrayPop out in the code editor (opens in a new tab)JavaScript
const drinks = ["tea"];
const nextDrinks = [...drinks];
nextDrinks.push("coffee");
console.log(drinks.length); // 1
console.log(nextDrinks.length); // 2

Line 2 makes a new array, nextDrinks, with the same items. Line 3 adds "coffee" to the new array only. So line 4 prints 1 and line 5 prints 2. If the array held objects, both arrays would still share those objects.

Good to know: push, sort, and reverse change the array you call them on. To keep the old array as it was, copy it first with [...drinks]. If you only need a sorted result, use toSorted(). It gives back a new sorted array and leaves the old one alone.

One shared object, or a new one?
  • const b = a; when a holds an object
  • const b = { score: a.score };
  • const b = { ...a };
  • const b = Object.assign({}, a);
  • const queue2 = queue1; when queue1 is an array
  • A function parameter that gets an object passed in
Try it yourself
0 of 6 correct

Sort each line. Does it leave two names pointing at one object, or does it make a new outer object?

Choose a category for every card. You can change an answer at any time; Reset clears them all.
New outer object, same inner objects

A shallow copy gives you a new outer object. Adding or changing a top-level property on the copy does not touch the original. But an object stored inside is still shared. Change something inside it, and both copies see the change.

Deep copies with structuredClone

A deep copy copies the objects stored inside too, at every level. Nothing is shared with the original. The built-in function structuredClone(value) makes a deep copy. It works in modern browsers and in recent versions of Node.

It can copy most kinds of data:

  • plain objects and arrays
  • dates (Date), and the collections Map and Set
  • many other built-in data types

It cannot copy functions or page elements (DOM nodes). If you try to copy a function, it throws an error called DataCloneError.

A deep copy can change on its ownPop out in the code editor (opens in a new tab)JavaScript
const settings = { theme: { color: "blue" } };
const copy = structuredClone(settings);
copy.theme.color = "green";
console.log(settings.theme.color); // blue
console.log(copy.theme.color); // green

Line 1 makes settings, with a theme object inside. Line 2 makes a deep copy, so the copy gets its own theme object. Line 3 changes the color in the copy only. Line 4 prints blue and line 5 prints green.

The table compares five ways to copy an object. Read each row as: "this way copies this much, and watch out for this."

Five ways to copy an object, and how much each one copies
How you copyWhat gets copiedWatch out
Plain assignment: const b = aOnly the reference. No new object is made.Both names point at the same object.
Spread: { ...a }The top-level properties, into a new object.Objects stored inside are still shared.
Object.assign({}, a)The top-level properties, into a new object.It is shallow, just like spread.
structuredClone(a)Everything, at every level.It can't copy functions or page elements (DOM nodes).
JSON.parse(JSON.stringify(a))Only data that JSON text can hold.Dates turn into strings. undefined values and functions are dropped.

Use structuredClone only when you really need a fully separate copy. Don't use it for every change:

  • A deep copy takes more work, because it copies everything.
  • Some objects, like a page element, should not be copied at all.
  • If you change only one part, you can copy just the objects on the way to that part. The "Where you'll use this" section shows how.

const doesn’t freeze an object

The Variables lesson introduced const. It means you can't give the variable a new value later. It does not lock the object. You can still change the properties of a const object, because the variable keeps pointing at the same object.

The variable can't change, but the object canPop out in the code editor (opens in a new tab)JavaScript
const user = { name: "Ada" };user.name = "Grace";     // allowed// user = { name: "Lin" }; // TypeError if you run itconsole.log(user.name);   // "Grace"

Line 1 makes a user object. Line 2 changes its name, and that is allowed. Line 3 is turned off with //. If you ran it, it would throw a TypeError, because it tries to point user at a new object. Line 4 prints "Grace".

To stop changes to the object itself, there is a separate tool: Object.freeze(user). It only locks the outer object, not the objects stored inside it. The Freezing, sealing & immutability lesson covers it later.

Where you’ll use this

References matter whenever two parts of a program use the same data. Some everyday examples:

  • A function gets a settings object. If it changes settings.theme, the object you passed in changes too.
  • Many page frameworks only notice a change when they get a new object. So code often makes a shallow copy with the change, instead of changing the old object.
  • Tests usually compare what is inside two objects, not whether they are the same object. Two correct functions can return objects that look alike but are different objects.
  • Sending data between browser windows or workers copies it with the same rules as structuredClone. The Messaging & structured cloning lesson covers this later.

Take care when you change something deep inside an object. Say a cart has an item object inside it. If you copy only the cart, the old cart and the new cart still share that item. So make a new object at each level you change, and put the new value in.

Change one item's quantity, and keep the old cart as it wasPop out in the code editor (opens in a new tab)JavaScript
const cart = {  item: { name: "tea", quantity: 1 },}; const nextCart = {  ...cart,  item: { ...cart.item, quantity: 2 },}; console.log(cart.item.quantity);console.log(nextCart.item.quantity);

Lines 1 to 3 make a cart with one item, tea, with quantity 1. Line 5 starts a new object, nextCart. Line 6 copies the cart's properties into it. Line 7 replaces item with a new item object: it copies the old item's properties, then sets quantity to 2. Line 10 prints 1 and line 11 prints 2. The old item never changed.

Common mistakes

“Assigning an object makes a copy of it.”

No. const b = a copies the reference. You get a new object only when you make one, for example with { ...a }.

“JavaScript passes objects by reference.”

Not quite. A function can change the properties of an object you pass in. But if it gives its parameter a new object, your variable doesn't change. The right name for this is "call by sharing".

“Two objects with the same properties are equal.”

Not with ===. For objects, === only checks whether both sides are the same object. To compare what is inside, write a check that looks at the keys and values.

“Spread makes a deep copy.”

No. Spread and Object.assign make shallow copies. Objects stored inside are still shared, unless you copy them too.

“const stops an object from changing.”

No. const only stops the variable from getting a new value. It stops user = ..., but not user.name = ....

Practice: objects and references

6 EXERCISES

Before each one, picture the arrows: which variables point at which object? Then ask what each line does. Does it change something inside an object, point a variable at a new value, or make a copy?

Exercise 1 · Warm-upPredict the shared change

Follow the arrows from a and b. What number does the code print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
const a = { score: 1 };
const b = a;
b.score = 5;
console.log(a.score);

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

    Exercise 2 · PracticeFix a function that changes its input

    This function renames a user by changing the object it gets. Rewrite it so it returns a renamed copy instead, and the object passed in stays the same. What does the function return?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    function renameUser(user, name) {
      user.name = name;
      return user;
    }

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

      Exercise 3 · PracticeMake a separate copy of settings with an object inside

      A settings object has a theme object inside it. Make a copy, then change the color in the copy only. What call makes the copy?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const copy = structuredClone(settings);
      copy.theme.color = "blue";

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

        Exercise 4 · ChallengeCompare two flat objects

        Read this function. It should return true for two flat objects with the same keys and values. What does it return for { a: 1 } and { a: 1 }?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        function sameFlatObject(left, right) {
          const leftKeys = Object.keys(left);
          const rightKeys = Object.keys(right);
          return leftKeys.length === rightKeys.length &&
            leftKeys.every((key) => Object.hasOwn(right, key) && Object.is(left[key], right[key]));
        }

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

          Exercise 5 · PracticeExplain the const surprise

          Run the code. Then explain in a few words why const lets line 2 change the name.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const pet = { name: "Momo" };
          pet.name = "Pip";
          console.log(pet.name);

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

            Exercise 6 · ChallengeChange a cart item in a real app

            A shopping page must change the quantity of one cart item. The old cart must stay as it was. Complete the function, then call it with a cart that holds { item: { name: "tea", quantity: 1 } } and the new quantity 2. Which property needs its own spread?

            Starter codePop out in the code editor (opens in a new tab)JavaScript
            function setQuantity(cart, quantity) {
              return {
                ...cart,
                item: { ...cart.item, quantity },
              };
            }

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

              Quiz: check your understanding

              8 QUESTIONS

              Pick an answer, then read the explanation for every choice. The wrong choices show common mistakes. For copy questions, ask first: is the changed property at the top level, or inside an object stored inside?

              Lesson quiz · 8 questionsScore: first tries count
              1. Question 1 of 8What does this code print when two names share one object?

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

                Choose an answer to see the explanation.

              2. Question 2 of 8What happens when you pass an object to a function?

                Choose an answer to see the explanation.

              3. Question 3 of 8What does === check when both sides are objects?

                Choose an answer to see the explanation.

              4. Question 4 of 8What does this comparison print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const a = { item: "tea" };
                const b = { item: "tea" };
                console.log(a === b);

                Choose an answer to see the explanation.

              5. Question 5 of 8What does a shallow copy with spread copy?

                Choose an answer to see the explanation.

              6. Question 6 of 8What does this spread copy print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const user = { delivery: { note: "Leave at door" } };
                const copy = { ...user };
                copy.delivery.note = "Ring bell";
                console.log(user.delivery.note);

                Choose an answer to see the explanation.

              7. Question 7 of 8Which of these can structuredClone copy?

                Choose an answer to see the explanation.

              8. Question 8 of 8What does const lock in const user = { name: "Ada" }?

                Choose an answer to see the explanation.

              Key takeaways

              A variable that holds an object holds a reference to it. Copies, function calls, comparisons, and const all follow from that one rule.

              • A variable that holds an object holds a reference: a way to find the object.
              • Copying that variable copies the reference. It does not make a new object.
              • A function can change the properties of an object you pass in. Giving its parameter a new object changes only the parameter.
              • For objects, === checks whether both sides are the same object. It does not compare what is inside.
              • Spread and Object.assign make shallow copies: objects stored inside are still shared. structuredClone makes a deep copy.
              • const stops the variable from getting a new value. It does not stop changes inside the object.

              Remember the one-liner.
              A variable that holds an object holds the way to find it, and two variables can find the same one.

              Up next: Methods & this. Objects can hold functions too, and you'll learn how a function knows which object it belongs to.

              CompleteFrontend Clear concepts. Working examples.