JavaScript is pass-by-value, but for objects the copied value is a reference to the same object. That is why object property mutations can be visible outside a function, while reassignment is not. Interview-quality answers explain call-by-sharing with concrete mutation vs reassignment examples.
Passing by value vs passing by reference in JavaScript
The Core Idea
JavaScript arguments are passed by value. Always.
The confusion starts because object values are references (addresses). So when you pass an object, JavaScript copies the reference value, not the whole object. Both caller and callee can point to the same object in memory.
This model is often called call-by-sharing.
Case | What gets copied into the parameter | If you mutate inside function | If you reassign the parameter |
|---|---|---|---|
Primitive (number/string/boolean/etc.) | The primitive value itself | Not applicable (primitives are immutable) | Only local parameter changes |
Object/Array/Function | A reference value to the same object | Visible outside (same underlying object) | Only local parameter points elsewhere |
// Primitive example: local change only
function bump(x) {
x = x + 1;
return x;
}
let count = 10;
const next = bump(count);
console.log(next); // 11
console.log(count); // 10 (unchanged)
Objects: Mutation vs Reassignment
For objects, these two lines are very different:
obj.name = 'X'-> mutates shared objectobj = { name: 'X' }-> rebinds local parameter only
function updateUser(user) {
user.name = 'Ada'; // mutation: affects caller
}
function replaceUser(user) {
user = { name: 'Grace' }; // reassignment: local only
}
const original = { name: 'Lin' };
updateUser(original);
console.log(original.name); // 'Ada'
replaceUser(original);
console.log(original.name); // still 'Ada'
function addItem(list) {
list.push('x'); // mutation: visible outside
}
function resetList(list) {
list = []; // reassignment: local only
}
const items = ['a', 'b'];
addItem(items);
console.log(items); // ['a', 'b', 'x']
resetList(items);
console.log(items); // ['a', 'b', 'x']
How to Avoid Side Effects
If a function should not alter caller state, create a new value inside the function and return it. Use shallow or deep copy strategy based on data shape.
function safeRename(user, nextName) {
return { ...user, name: nextName }; // non-mutating top-level update
}
const a = { name: 'Lin', meta: { role: 'dev' } };
const b = safeRename(a, 'Ada');
console.log(a.name); // 'Lin'
console.log(b.name); // 'Ada'
Common Pitfalls
- Saying 'objects are passed by reference' as a full explanation (incomplete).
- Forgetting that spread/object copy is shallow, so nested objects remain shared.
- Accidentally mutating function inputs in reducers, hooks, or utility helpers.
- Misusing
const: it prevents rebinding, not internal object mutation.
Interview prompt | Strong answer pattern |
|---|---|
Is JS pass-by-value or pass-by-reference? | Pass-by-value; object values are references, so copied references can mutate shared objects. |
Why did my function change external state? | Because it mutated a shared object via copied reference value. |
How do you prevent this? | Return new values (immutable updates), and test nested references explicitly. |
Practical scenario
A pricing helper receives a cart object and unexpectedly mutates line items, causing stale UI and failed snapshot tests in checkout flows.
Common pitfalls
- Mutating objects passed into utility functions.
- Assuming reassignment and mutation are equivalent.
- Only testing return values, not input integrity.
In critical paths, add tests that assert original inputs remain unchanged (deep equality + reference checks where needed).
Think of giving someone your home address, not your house. If they repaint the house, both of you see it changed. If they write down a different address, your house stays the same.
JavaScript uses pass-by-value for all arguments. For primitives, that copied value is the data itself. For objects, that copied value is a reference to shared data. So mutation can leak across scopes, while parameter reassignment stays local.
Use the relevant interview-question hub first, then move into a concrete study plan before targeted company sets.