Question
JavaScript Pass by Value: Objects, References, and Function Arguments
Question
JavaScript primitive values such as numbers and strings are commonly described as being passed by value. However, object arguments can appear to behave differently: a function can mutate an object supplied by the caller, but it cannot replace the caller's variable by assigning a new object to its parameter.
What is the correct way to describe JavaScript's argument-passing convention? Is JavaScript pass-by-value or pass-by-reference, and how does the ECMAScript specification define the relevant semantics?
Short Answer
JavaScript is pass-by-value. When you pass an object to a function, the value being copied is an object reference value. Both the caller's variable and the function parameter can therefore refer to the same object and mutate it, but reassigning the parameter changes only the local copy.
Concept
JavaScript function arguments are passed by value.
A value can be either:
- A primitive value, such as a
number,string,boolean,bigint,symbol,undefined, ornull. - An object reference value: a value that identifies an object in memory.
For primitives, copying the value is easy to observe because primitives are immutable:
function increase(value) {
value = value + 1;
}
let score = 10;
increase(score);
console.log(score); // 10
score is unchanged because the function receives its own copy of 10.
For objects, JavaScript also copies a value—but that value refers to an object. After the copy, the caller's variable and the parameter refer to the same object:
function rename(user) {
user.name = "Ada";
}
const person = { name: "Grace" };
(person);
.(person.);
Mental Model
Think of a variable as a label containing a value.
- For a primitive, the label contains the data itself, such as
42. - For an object, the label contains a map location that leads to the object.
When calling a function, JavaScript photocopies the value on the label into a new label—the parameter.
For an object, photocopying the map location means both labels lead to the same house. Either person can repaint the house (mutate its properties). But if the function replaces its own map with directions to a different house (reassigns the parameter), the caller still has the original map.
Syntax and Examples
A parameter is a local variable initialized with the argument's value.
function inspect(item) {
// `item` starts with a copy of the argument value.
console.log(item);
}
inspect("hello");
Primitive argument
function setToZero(number) {
number = 0;
}
let count = 5;
setToZero(count);
console.log(count); // 5
The parameter number is separate from count.
Object mutation
function markComplete(task) {
task.completed = true;
}
const task = { title: "Write tests", completed: false };
markComplete(task);
.(task.);
Step by Step Execution
Trace this example:
function updateProfile(profile) {
profile.role = "admin";
profile = { name: "Lin", role: "guest" };
profile.role = "editor";
}
const account = { name: "Lin", role: "member" };
updateProfile(account);
console.log(account); // { name: "Lin", role: "admin" }
accountcontains an object reference value for the object{ name: "Lin", role: "member" }.updateProfile(account)copies that reference value into the local parameterprofile.profile.role = "admin"mutates the original shared object.accountcan observe this change.profile = { name: "Lin", role: "guest" }assigns a new object reference to the localprofilebinding only.- mutates the new local object, not the object held by .
Real World Use Cases
- Updating state objects: A function may add a field to a request context, such as
request.user. - Modifying arrays: Functions can mutate a passed array with
push,sort, orsplice. - API payload construction: A helper can add headers or metadata to a shared options object.
- Configuration handling: A function may intentionally clone a configuration object before changing it, preventing unexpected changes for the caller.
- DOM work: A function can change a passed DOM element's text or classes because the parameter refers to the same element object.
Example: intentionally avoiding mutation in an application setting:
function addItem(cart, item) {
return [...cart, item];
}
const currentCart = ["book"];
const nextCart = addItem(currentCart, "pen");
console.log(currentCart); // ["book"]
console.log(nextCart); // ["book", "pen"]
The function returns a new array rather than mutating the caller's array.
Real Codebase Usage
Developers use this behavior deliberately and protect against accidental mutation.
Validate without changing input
function isValidEmail(email) {
return typeof email === "string" && email.includes("@");
}
Primitive arguments are naturally safe from mutation because strings are immutable.
Use guard clauses before mutation
function applyDiscount(order, percentage) {
if (!order || percentage <= 0) {
return;
}
order.total *= 1 - percentage / 100;
}
This intentionally changes the order object only after validation.
Return a copy for predictable state updates
function updateUserName(user, name) {
return {
...user,
name
};
}
This pattern is common in UI state management and reducers because it avoids changing the input object.
Clone nested data when necessary
Common Mistakes
Calling object behavior pass-by-reference
This description is misleading:
"Objects are passed by reference."
It can suggest that a parameter is an alias for the caller's variable. In JavaScript, it is not. Prefer:
"JavaScript passes values. For objects, the value is a reference to the object."
Expecting parameter reassignment to update the caller
function clearList(list) {
list = [];
}
const names = ["Ada", "Lin"];
clearList(names);
console.log(names); // ["Ada", "Lin"]
To intentionally empty the existing array, mutate it:
function clearList(list) {
list.length = 0;
}
Or return a replacement and assign it in the caller:
function emptyList() {
return [];
}
let names = [, ];
names = ();
Comparisons
| Idea | What the function receives | Can it mutate the caller's object? | Can parameter reassignment replace the caller's variable? |
|---|---|---|---|
| JavaScript primitive argument | A copied primitive value | Not applicable | No |
| JavaScript object argument | A copied object reference value | Yes, through the shared object | No |
| True pass-by-reference | Access to the caller's variable/location | Yes, if the value supports mutation | Yes |
Mutation vs reassignment
| Operation | Example | Affects caller's object? |
|---|---|---|
| Property mutation | user.name = "Ada" | Yes, when both values refer to the same object |
Cheat Sheet
-
JavaScript passes all function arguments by value.
-
Primitive values are copied directly.
-
An object value is a reference to an object; that reference value is copied.
-
A function can mutate a shared object:
function f(obj) { obj.x = 1; } -
A function cannot reassign the caller's variable:
function f(obj) { obj = {}; } -
constprevents variable reassignment, not object mutation. -
Spread syntax (
{ ...obj },[...array]) makes a shallow copy. -
Return a new object or array when callers should receive a replacement.
-
Avoid saying JavaScript "passes objects by reference" without clarification; say it passes object reference values by value.
FAQ
Is JavaScript pass-by-value or pass-by-reference?
JavaScript is pass-by-value. Object reference values are copied when passed to functions.
Why can a function change an object passed to it?
The parameter and the caller's variable refer to the same object. Changing a property changes that shared object.
Why does assigning a new object to a parameter not change the original variable?
The parameter is a separate local binding containing a copied value. Reassignment changes only that local binding.
Are arrays passed by reference in JavaScript?
Arrays are objects, so they follow the same rule: JavaScript passes a copied reference value. Array mutations are visible to the caller; parameter reassignment is not.
Are strings passed by reference in JavaScript?
No. Strings are primitive values and are passed by value. Also, strings are immutable.
Does const stop a function from changing an object?
No. const stops rebinding the variable to another value. It does not freeze the object or its properties.
How can I prevent a function from mutating my object?
Use a copying convention, pass a clone, or use Object.freeze when appropriate. For nested mutable data, remember that a shallow copy does not clone nested objects.
Does the ECMAScript specification call this pass-by-value?
The specification defines behavior through argument evaluation, values, and parameter bindings rather than using that informal label. Its defined behavior matches pass-by-value: arguments become values used to initialize local parameter bindings.
Mini Project
Description
Build a small profile-update utility that demonstrates the difference between mutating a passed object and returning a replacement object. This mirrors a common decision in application code: should a helper update shared data, or should it create the next version of the data?
Goal
Create functions that update a user profile in both mutable and immutable styles, then verify the different results.
Requirements
Define a user object with a name, role, and settings object.
Keep learning
Related questions
Abort Ajax Requests with jQuery jqXHR.abort()
Learn how to cancel an in-progress jQuery Ajax request with jqXHR.abort(), handle abort status safely, and avoid stale UI updates.
Access the Correct this Inside a JavaScript Callback
Learn why JavaScript this changes in callbacks and how to preserve an object context using bind, arrow functions, and event handler patterns.
Add Key-Value Pairs to JavaScript Objects
Learn how to add key-value pairs to JavaScript objects with dot and bracket notation, dynamic keys, examples, and common mistakes.