Objects: Reference vs Value
Reference vs. Value
TLDR: In JavaScript, primitive types deal with values, whereas objects, arrays, sets, or maps work with reference!
What is passing by value?
We talked about a bunch of types in JavaScript in my earlier posts. String, numbers or booleans work by values. What do I mean by that?
let person = "Parwinder";
let human = person;
console.log(person); // Parwinder
console.log(human); // Parwinder
person = "Ricky";
console.log(person); // Ricky
console.log(human); // Parwinder
I created a variable person
and assigned it a value. The variable human
was equal to the variable person
but that does not mean that human
reflects changes in person
. When I made human
equal to person
I did that by passing the value of person
to human
. A copy was made, and they are not related to each other. This is passing by value.
What is passing by reference?
Objects, arrays, sets, and maps work with reference and not by value.
let personObject = {
firstName: "Parwinder",
lastName: "Bhagat"
};
let humanObject = personObject;
console.log(personObject.firstName); // Parwinder
console.log(humanObject.firstName); // Parwinder
personObject.firstName = "Ricky";
console.log(personObject.firstName); // Ricky
console.log(humanObject.firstName); // Ricky
Did you notice the difference? The change to firstName
of personObject
reflects in the firstName
of humanObject
. That is because when I created humanObject
and made it equal to personObject
, it did not copy over the object. Instead, it created a reference to the personObject
. Since both the objects point to the same reference, a change made to the reference reflects in both.
Passing by reference is not only limited to copying information. It goes beyond. One such example would be calling a function. When you call a function by passing a variable that is string, number, or boolean, it passes the value. So if we change the passed value somewhere in the function, the original value does not get impacted.
On the other hand, if I pass an Object to a function, and within the function, I change a property of the passed object, the original object gets impacted. The original object reflects the changed value now.
In case of a primitive type
function changeValue(arg) {
arg = "This is a new value";
return arg;
}
let person = "Parwinder"
console.log(changeValue(person)); // This is a new value
console.log(person); // Parwinder
You can see that the variable person
did not change when I performed an operation on variable/argument arg
.
In case of object
function changeValue(arg) {
arg.name = "Ricky";
return arg;
}
let person = {
name: "Parwinder",
age: 33
}
console.log(changeValue(person)); // { name: 'Ricky', age: 33 }
console.log(person); // { name: 'Ricky', age: 33 }
Whereas here, changing the name in the function did change the original object! 😱
Then, how do I copy objects?
If you want to copy an object’s values and not work with reference, you need to clone the original object. You can do this by using the spread (…) operator.
let personObject = {
firstName: "Parwinder",
lastName: "Bhagat"
};
let humanObject = { ...personObject };
console.log(personObject.firstName); // Parwinder
console.log(humanObject.firstName); // Parwinder
personObject.firstName = "Ricky";
console.log(personObject.firstName); // Ricky
console.log(humanObject.firstName); // Parwinder
You can see that humanObject
is a copy of personObject
because when I switched the firstName
property, it only did that change to personObject
. The change did not propagate to humanObject
!
Is it that simple?
The short answer is no. What we did above using the spread operator is we made a shallow copy of the object. Shallow copy copies the first level properties of the object. Properties deeper than the first level are still referenced!
let personObject = {
firstName: "Parwinder",
lastName: "Bhagat",
vehicles: {
car: "Honda Civic",
bike: "Honda Rebel"
}
};
let humanObject = { ...personObject };
console.log(personObject.vehicles.car); // Honda Civic
console.log(humanObject.vehicles.car); // Honda Civic
personObject.firstName = "Ricky";
console.log(personObject.firstName); // Ricky
console.log(humanObject.firstName); // Parwinder
personObject.vehicles.car = "BMW X5";
console.log(personObject.vehicles.car); // BMW X5
console.log(humanObject.vehicles.car); // BMW X5
In the above example, I made a shallow copy, and when I switched the name in one object, it did not change in the other (as expected). But when I change the car which is not on the first level of the object, it gets changed in the other object. Remember, shallow copy only copies the first level, deeper levels are still by reference.