Difference between primitive and reference types
Difference between primitive and reference types
Primitive types are number, string, boolean, null, undefined, symbol, and bigint. These values are immutable and compared by value. When you assign a primitive to a new variable or pass it to a function, a copy of the value is made. Changing the new variable does not affect the original.
Reference types are objects, including arrays and functions. These values are stored by reference. When you assign an object to another variable, both variables refer to the same underlying object. Changing a property through one reference is visible through the other. Equality comparisons for objects check whether two references point to the same object, not whether their contents are
equal.
Stack and heap are common mental models. Engines are free to implement however they like, but the model is useful: primitives are often small fixed size values and can be copied easily, while objects may live in managed memory with garbage collection and are accessed by references.
Example
// primitives copy by value
let a = 5;
let b = a;
b = 7;
console.log(a, b); // 5 7
// objects copy by reference
const p = { n: 1 };
const q = p;
q.n = 2;
console.log(p.n); // 2
// equality
console.log({} === {}); // false, different objects
const r = {};
const s = r;
console.log(r === s); // true, same reference
// shallow copy vs deep copy
const user = { name: "A", meta: { views: 1 } };
const shallow = { ...user }; // shallow copy
shallow.meta.views = 5;
console.log(user.meta.views); // 5
Common misconceptions
- Objects are passed by reference. JavaScript passes arguments by value; the value for objects is a reference. Reassigning the parameter does not change the caller's variable, but mutating the object does.
- Spreading or Object.assign always creates a deep copy. They only copy one level by default.
- Two objects with the same properties are equal with ===. They are equal only if they are the same reference.
Practice questions
- Show how to deep clone a nested object without mutating the original using structuredClone or a library.
- Explain why a function cannot reassign an object parameter to replace the caller's variable but can mutate its properties.
- Give an example where a shallow copy causes an unexpected mutation in the original object.