learn
JavaScript

Values, references and copying

Primitives copy, objects share — plus shallow versus deep copies that hold up.

Assigning an object doesn't duplicate it; it hands out another key to the same locker. Interviewers ask this because shared-reference bugs — state edited through an "independent" copy — are the most common UI defects there are.

Real-life situation

A receipt view aliases the live cart object to format it. Adding a "free gift" line to the receipt silently rewrites the cart total, and the customer is charged for the gift. The fix: format a real copy, so the receipt can never reach back into the cart.

Seen in: React state updates replace objects instead of mutating them, so change detection sees a new reference (React 16.8, 2019, useState reference).

Visual

Value types
BrowserAPIRealtimeCacheDatabaseShareddurableephemeral

Try it

Alias, shallow copy, deep copy
javascriptTab moves to the next control
const cart = { items: ["book"], total: 20 };
const receipt = cart;
receipt.items.push("pen");
receipt.total = 25;
console.log(cart.items.length);
console.log(cart.total);
const frozen = { items: ["book"], total: 20 };
const copy = { items: frozen.items.slice(), total: frozen.total };
copy.items.push("pen");
copy.total = 25;
console.log(frozen.items.length);
console.log(frozen.total);
const deep = structuredClone(cart);
deep.items.pop();
console.log(cart.items.length);
console.log(deep.items.length);
Press Run to see the output.
Expected output
2
25
1
20
2
1
Runner helpers

sleep(ms) waits; fakeFetch(url) returns deterministic sample data. Console code runs in a worker with a 5-second limit.

Try changing receipt to a spread copy and watch the first two lines. Then change the spread copy to share items and watch them break again.

When to copy how

Flat data: spread or slice. Nested data you edit: spread every edited level, or structuredClone the whole tree. Data with functions or class instances: copy by hand, because cloners drop behavior.

Shared nesting — guess first, then reveal.

predict.js
const user = { name: "Ada", address: { city: "Lagos" } };
const copy = { ...user };
copy.address.city = "Abuja";
console.log(user.address.city);

Interview questions

If two variables point at the same object, what happens when you change one?basic

Both see the change, because neither variable holds the object — each holds a reference to one shared object in memory. This is why pushing into an array passed as an argument mutates the caller's array. When you need independence, create a new object: spread for one level, structuredClone for the whole tree.

Interviewers also ask

  • How does this bite in React state updates?
What is the difference between a shallow copy and a deep copy?basic

A shallow copy duplicates only the top level — nested objects are still shared references, so editing copy.address.city changes the original too. A deep copy duplicates every level, giving full independence. Spread ({...obj}) and Array.prototype.slice are shallow; structuredClone is a deep copy built into the language. Match the tool to how deep the edits go.

Interviewers also ask

  • What does structuredClone fail to copy?
What does structuredClone not handle?intermediate

structuredClone copies plain data beautifully but refuses functions (it throws), drops DOM nodes, and turns class instances into plain objects — methods and prototypes do not survive. It also cannot clone two objects that reference each other in some older engines, though modern ones keep the cycle. For state-shaped data it is the right default; for behavior (functions, class instances), design the copy by hand.

Interviewers also ask

  • How would you clone an object that holds a function?
Why does editing a nested object change the original after a spread copy?intermediate

Spread copies property values, and for a nested object the "value" is a reference to the same inner object — so the copy and the original share that inner object. The fix is to spread at every level you edit, or structuredClone the whole thing. A quick tell: if any level below the top is an object you mutate, one spread is not enough.

Interviewers also ask

  • Show the one-line fix for a two-level update.
How do you compare two objects for equality?basic

=== on objects only asks "same reference?", so two identical-looking objects are unequal. For a quick check, compare the fields you care about directly, or JSON.stringify both (fragile: key order and undefined values bite). Libraries offer deep-equal helpers, but in interviews the expected answer is usually: compare field by field, or avoid needing to by comparing ids.

Interviewers also ask

  • Why is JSON.stringify unreliable for this?
Why is mutating function arguments considered bad practice?intermediate

Callers rarely expect their data changed as a side effect — a sort(cart) that reorders the caller's array causes bugs far from the call site and breaksUI frameworks that detect change by reference. Pure functions that return new values are predictable and testable. When mutation is the point (a cache, a builder), name the function so the mutation is obvious.

Interviewers also ask

  • Which array methods mutate and which return new arrays?

Key takeaways

  • Primitives copy by value; objects copy by reference.
  • Spread is shallow: nested objects stay shared.
  • structuredClone deep-copies data, but drops functions and prototypes.

References

Last verified: 2026-10-12.

On this page