Primitives vs. References
Copying a number copies the value; copying an object copies a pointer to the same shared value.
Loading visual…
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 10b 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 99obj2 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.
`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.