← All concepts
Behind the Scenes

Primitives vs. References

Copying a number copies the value; copying an object copies a pointer to the same shared value.

Definition

Primitive values (strings, numbers, booleans, null, undefined, symbols, bigints) are copied by value. When you assign a primitive to a new variable or pass it into a function, the engine duplicates the value itself, so the two variables are completely independent from that point on. Changing one never affects the other. Objects and arrays are copied by reference. A variable holding an object does not hold the object's data directly; it holds a reference (effectively an address) pointing at the object in memory. Assigning that variable to another variable, or passing it into a function, copies the reference, not the object, so both variables end up pointing at the exact same object. Mutating the object through either variable, such as setting a property, is visible through both, because there is only ever one object being shared.

Examples

let a = 5;
let b = a;
b = 10;
console.log(a, b); // 5 10

b is a separate copy of the number 5. Reassigning b has no effect on a.

const obj1 = { count: 1 };
const obj2 = obj1;
obj2.count = 99;
console.log(obj1.count); // 99

const obj3 = { ...obj1 }; // shallow copy: a new, separate object
obj3.count = 1;
console.log(obj1.count); // still 99

obj2 points at the same object as obj1, so mutating through obj2 is visible on obj1. Spreading into obj3 creates a genuinely separate top-level object instead.

Common mistakes

  • Assigning an object to a new variable and expecting a separate copy, when both variables actually point at the same shared object.
  • Using `{ ...obj }` to copy nested objects and assuming it is a deep copy; a spread only copies the top level, so nested objects are still shared.
  • Comparing two objects with `===` and expecting it to check their contents, when it actually checks whether they are the same reference.

Key takeaways

  • Primitives are copied by value, so two variables holding a primitive are fully independent.
  • Objects and arrays are copied by reference, so assigning or passing one just copies a pointer to the same data.
  • Mutating an object through one reference is visible through every other reference to it.
  • A shallow copy like spread separates the top-level object but still shares any nested objects inside it.
Check your understanding

`const obj1 = { count: 1 }; const obj2 = obj1; obj2.count = 99;` What is `obj1.count` afterward?

Where you see this

  • Explaining why mutating a React state object directly does not trigger a re-render, and why { ...state } is used instead.
  • Debugging a bug where updating 'a copy' of an object also changed the original.
  • Choosing structuredClone or a deep copy utility when nested objects need real independence.
  • Understanding why comparing two objects with === checks reference identity, not their contents.

Practice this

Further reading

Previous
Execution Context and the Call Stack
Next
Destructuring