I once failed an interview because I didn’t explain enough about how certain data types are passed to a function. It seemed like a small detail at the time, but in reality, it plays a big role in how we write and debug code.
That’s exactly what these two methods describe—how data gets passed to a function. Understanding this can help you avoid unexpected bugs and write more efficient code.
In this post, I’ll explain which types of data fall under each method and why it’s important to know the differences.
Pass by Value
Primitive data types—like strings
, numbers
, and booleans
—are always passed by value. But what does that actually mean? Let’s look at an example:
function modify(val) {
val = 100;
}
let num = 42;
modify(num);
console.log(num); // Output: 42 (original value unchanged)
“Pass by value” means that only a copy of the value is passed to the function. This means any modifications inside the function do not affect the original value. In our example, the num
variable is initialized with the number 42
. When we call modify(num)
, the function receives a copy of num
and reassigns it to 100
. However, this change has no effect on the original value of num
.
The same thing happens with strings:
function modify(val) {
val = "one-hundred";
}
let num = "forty-two";
modify(num);
console.log(num); // Output: "forty-two" (original value unchanged)
Since strings are also primitive data types, only a copy is passed to the function, meaning the original value remains unchanged.
Pass by reference
Non-primitive data types, like objects
, arrays
, Sets
, or functions
, always fall under “pass by reference.” This means that a copy of the reference to the memory location is passed to the function. Therefore, if the function modifies the given parameter, the original value is also changed.
function modifyObject(ref) {
ref.value = 10;
}
let obj = { value: 5 };
modifyObject(obj);
console.log(obj.value); // Output: 10 (original object changed)
When we pass the obj
object to the modifyObject
function, only a copy of the reference to the memory location of the obj
object is passed. However, since both the argument and the original object point to the same memory location, the original value gets changed.
To prevent modifying the original value, it’s recommended to return a new object and save it in a new variable:
function modifyObject(ref) {
return { ...ref, value: 10 }; // Creates a new object instead of modifying ref directly
}
let obj = { value: 5 };
let newObj = modifyObject(obj); // Store the returned new object
console.log(obj.value); // 5 (unchanged)
console.log(newObj.value); // 10 (new object with the updated value)
By doing this, the original object isn’t affected or changed.
Stack and Heap Memory
Now we know how primitive and non primitive data types get passed to a function, but what about the why?
Why do primitives and non primitives behave differently when passed to a function?
Essentially it all comes down to how and where they are saved in memory.
Stack Memory
The stack memory is a simple and fast, but not that big, structure. Perfect for storing primitive types like strings
, numbers
and booleans
. Since all of these types have a fixed size, it’s easy to copy them.
function modifyNumber(num) {
num = 100; // Changing the local copy of num
}
let myNum = 42;
modifyNumber(myNum);
console.log(myNum); // Output: 42 (original value remains unchanged)
In this example:
myNum
is a primitive data typenumber
stored in stack memory.When passed to
modifyNumber
, a copy of the value42
is placed on the stack.Changing the value inside the function only affects the local copy, not the original variable outside the function.
Heap Memory
Heap memory is a larger, slower, and more flexible structure. It’s perfect for storing large arrays
or objects
that contain multiple elements or properties.
When you create an object, the variable only holds a reference to the memory location, while the actual object is stored in the heap:
let obj = { name: "Hamza" };
Stack: | Heap:
--------------- | ------------------
obj (ref: #123) | #123 → { name: "Hamza" }
Non-primitive data types, like objects
and arrays
, are stored in the heap, not the stack. This is because of their varying sizes. While an object can start with just one property, it could also have hundreds of entries, such as an API response. Copying these large structures in the stack (i.e., passing by value) would be costly in terms of memory and performance.
Conclusion
In this post, we explored the differences between "pass by value" and "pass by reference" in JavaScript, and how these concepts relate to stack and heap memory. Understanding how data is passed to functions can help you write more efficient code, avoid unintended side effects, and make better decisions when working with both primitive and non-primitive data types.
Whether you’re dealing with small primitive values or large objects, knowing where and how they’re stored in memory is key to mastering JavaScript's behavior.
Thanks for reading! Hope this helped and happy coding! 🚀