· Tech · 7 min read
Is JavaScript pass-by-value or pass-by-reference?
A pragmatic explanation of how JavaScript handles values and references, with clear examples and mental models for understanding assignment and equality.
TL;DR
Is JavaScript pass-by-value or pass-by-reference? It’s a classic debate that trips up even experienced devs. The answer isn’t as simple as you might think—let’s break it down together!
Yet Another JavaScript Blog Post
There are a lot of tech articles out there, seemingly narrating the same topics over and over again. Are they worth it? They are, in fact, worth it for the writers. Writing articles is a great way to solidify your understanding of a topic. And it air’nt gonna hurt any readers either.
That is the premise I am taking a courage from to produce yet another article (or more) about JavaScript.
Is JavaScript pass-by-value or pass-by-reference?
JavaScript is a pass-by-value language for primitive types (such as numbers, strings, and booleans) and pass-by-reference for objects and arrays. This means that when you assign a primitive value to a variable, a copy of that value is made. However, when you assign an object or array to a variable, a reference to the original object or array is created.
^^^ The above sentence is a typical explanation for this topic, and before I knew it AI generated that sentence and I adapted it. (writing this in VS Code with Copilot). It is basically okay. It is valid. And we will look into how this works as well as the possibility of other models that describes the behavior simpler and quicker which is a little more pragmatic to reason about.
But first, let’s clarify what “pass-by-value” and “pass-by-reference” mean.
Let’s start with a simple example of pass-by-value:
let a = 10;
let b = a;
console.log(a); // 10
console.log(b); // 10
a = 20;
console.log(a); // 20
console.log(b); // 10What is happening here is simple. We create a, assign a value of 10 to it, then assign a to b. That time, the value of 10 is copied over to b. Then we reassign a value of 20 to a. Now a is 20, but b is still 10.
Then, consider this scenario:
let user = { name: "Josh", age: 30 };
let user2 = user;
user2.age = 24;
console.log("user.age: ", user.age); // what is the age now?In this case, we create an object user with properties name and age. Then we assign user to user2. Since user is an object, we are not copying the value, but rather creating a reference to the original object.
Therefore, the answer to the question “what is the age now?” is 24. Because we changed the age property of user2, which is a reference to the same object as user.
Usually that’s the end of the story. And that’s fine. Only that what if…
What if I tell you that JavaScript is pass-by-value for everything (in a way)?
Disclaimer: What this is not is it’s being a technically precise representation of the JavaScript engine’s internal workings, it’s a pragmatic mental model that has proven effective for understanding and predicting behavior.
So, why I am telling “JavaScript is pass-by-value for everything”?
To begin with, let’s consider this simple scenario:
let user = { name: "Josh", age: 30 };
let user2 = user;
user2.age = 24;
console.log("user.age: ", user.age); // what is the age now?
user2 = { name: "Henry", age: 84 };
console.log("user2.age: ", user2.age); // 84
console.log("user.age: ", user.age); // what is the age now?
console.log("user.name: ", user.name); // what is the name now?Some of you might got it right away, but if you are like me, you might assumed it is 84. But, it you run it in your browser console, you will see that user.age is still 24, and user.name is still Josh.
The reason is that when we assigned the value of { name: 'Henry', age: 84 } to user2, we created a new reference to a new object, which is { name: 'Henry', age: 84 }.
The term reference is the key. It is a pointer to the actual data, which holds a reference to the memory location where the data is stored. Or rather an address to it. So the simplest model is like this:
let user = { name: "Josh", age: 30 }; // creates a new object, which is ` { name: 'Josh', age: 30 }` in memory/heap, and `user` holds an address to it; let's call it `000abc123`
let user2 = user; // `user2` now holds the same address as `user`, which is `000abc123`
user2.age = 24; // assign `24` to the `age` property of the object at address `000abc123`, which is the same object as `user`
console.log("user.age: ", user.age);
user2 = { name: "Henry", age: 84 }; // creates a new object `{ name: 'Henry', age: 84 }` in memory/heap, and `user2` now holds a new address, let's say `000def456`, while `user` still holds the address `000abc123`
console.log("user2.age: ", user2.age); // refers to the property `age` of the object located at `000def456`, which is `84
console.log("user.age: ", user.age); // refers to the property `age` of the object located at `000abc123`, which is `24`
console.log("user.name: ", user.name); // refers to the property `name` of the object located at `000abc123`, which is `Josh`Now, this notion of “address” is not exactly how JavaScript works under the hood. Technically, primitive values are stored in the “stack”, and objects data are stored in the “heap”. And how exactly it is implemented is beyond the scope of this article and depends on the engines, but this notion of “the address it is holding” is a good short cut to safely and confidently predicting how it works.
Let’s consider this:
const string1 = "hello";
const string2 = "hello";
console.log("string1 === string2: ", string1 === string2); // true
const object1 = { name: "Yoshi" };
const object2 = { name: "Yoshi" };
console.log("object1 === object2: ", object1 === object2); // falseThis is a typical example of explaining how referential equality works in “equality” check. When comparing string1 and string2, it is their value that is compared, so it is true. When comparing object1 and object2, it is their reference that is compared. Therefore, it is false.
const string1 = "hello";
const string2 = "hello";
console.log("string1 === string2: ", string1 === string2); // true
const object1 = { name: "Yoshi" };
const object2 = { name: "Yoshi" };
console.log("object1 === object2: ", object1 === object2); // falseTherefore, now we can interpret the above code as follows:
const string1 = "hello"; // string1's value is "hello"
const string2 = "hello"; // string2's value is also "hello"
console.log("string1 === string2: ", string1 === string2); // comparing "hello" and "hello", true
const object1 = { name: "Yoshi" }; // hypothetically object1's value is "000abc123" which is the address to the object `{ name: "Yoshi" }` in memory)
const object2 = { name: "Yoshi" }; // hypothetically object2's value is "000def456" which is the address to the object `{ name: "Yoshi" }` in memory)
console.log("object1 === object2: ", object1 === object2); // comparing "000abc123" and "000def456", false
let object3 = object1; // assigning "000abc123" to object3; it is let simply for the sake of experiments
console.log("object1 === object3: ", object1 === object3); // comparing "000abc123" and "000abc123", true
object3.name = "Henry"; // changing the name property of the object at address "000abc123" to "Henry"
console.log("object1.name: ", object1.name); // accessing the name property of the object at address "000abc123", which is now "Henry"
object3 = { name: "Jackson" }; // object3 now holds a new address, let's say "000def789", which is the address to the object `{ name: "Jackson" }` in memory
console.log("object1.name: ", object1.name); // object1 is intact, whose address is "000abc123", which still has the name of "Henry"
console.log("object3.name: ", object3.name); // accessing the name property of the object at address "000def789", which is "Jackson"
console.log("object1 === object3: ", object1 === object3); // now comparing "000abc123" and "000def789", false
This is a pragmatic mental model that has served me well so far, and I hope it does for you too when working with references. With React dominating the front-end world, you’ll likely find these concepts useful someday. Hopefully!
For me, they certainly saved the day, though I must admit I initially spent a few hours tearing my hair out because I had completely forgotten these concepts because of sleep deprivation because of my sweet little rascals at home.

