What's the difference?
Assigning things in JavaScript seems simple, but there's a little more that occurs behind the scenes that changes how assignment works. There's two ways of passing variables — by value and by reference.
Knowing when which occurs can easily be identified: passing by value occurs when dealing with any primitive data types (string, number, boolean, etc.), whereas passing by reference occurs when dealing with anything that is not a primitive data type (arrays and objects).
Passing by value
First, let's look at passing by value by creating a simple variable:
let a = 1;
...whenever anything is assigned in Javascript, it's allocated in memory. If we were to take a look inside the memory, it would look something like this...
Variable | Value |
---|---|
a | 1 |
...nothing unexpected. Let's add more to our code...
let a = 1;
let b = a;
let b = b + 7
...b
is assigned the value of a
, which is
1
. Then, we add 7
to b
.
Let's look at the memory...
Variable | Value |
---|---|
a | 1 |
b | 8 |
...wait, how come a
didn't change if we assigned it to b
and then added 7
? Well, the 1
in a
and the 1
in b
are two completely separate values.
They don't share the same piece of memory, so therefore, a
isn't mutated when we add
7
to b
.
Passing by reference
In the previous example we saw that primitive data types are passed by value, so each value gets its own distinct piece of memory. Simple to understand. This time, let's play around with a non-primitive data type, more specifically, an array:
let x = [1, 2, 3];
...if we take a look at this array in memory, it'll look something like this...
Variable | Value |
---|---|
x | 0x01 |
...wait, shouldn't the value of x
be [1, 2, 3]
?
Well, yes, but it doesn't get the value directly like primitive data types do. Instead, the value is a reference to an
address allocated in memory which holds the value, like so...
Address | Value |
---|---|
0x01 | [1, 2, 3] |
...now what if we try to duplicate and mutate the array like in the previous example...
let x = [1, 2, 3];
let y = x;
y.push(4);
...let's have a look at the memory...
Variable | Value |
---|---|
x | 0x01 |
y | 0x01 |
...this time, they both reference the same memory address; because of this, the changes made to
y
will also be applied
to x
...
Address | Value |
---|---|
0x01 | [1, 2, 3, 4] |
console.log(x);
// [1, 2, 3, 4]
console.log(y);
// [1, 2, 3, 4]
...as you might guess, this can present a lot of confusing bugs if you don't understand how passing works. If you try to change one variable, it might completely change another as well.
What if we duplicate the array again, but this time we completely overwrite it afterwards? Let's have a look:
let x = [1, 2, 3];
let y = x;
y = [4, 5, 6];
...this time, the variable is overwritten entirely, so a completely new memory address is allocated...
Variable | Value |
---|---|
x | 0x01 |
y | 0x02 |
...and each holds their own value...
Address | Value |
---|---|
0x01 | [1, 2, 3] |
0x02 | [4, 5, 6] |
...since each value has its own memory address, both x
or y
can be modified without affecting one another.
To prove this concept even further, I'll use the equality operator in order to compare two arrays:
let x = [1, 2, 3]; // 0x01
let y = [1, 2, 3]; // 0x02
x == y // false
x === y // false
...in this example, I've created two variables that hold the same value. Despite this, they both return as
false
when compared with both equality operators (remember, ==
only checks for value equality, whereas ===
checks for value equality AND data type equality).
This occurs because the values being compared are the memory addresses, not the actual values. Since the memory
addresses are completely different, the comparison outputs to false
.
To output to true
, both variables must share the same memory address, like so:
let x = [1, 2, 3]; // 0x01
let y = x; // 0x01
x == y // true
x === y // true
...both x
and y
share the same memory address
of 0x01
,
so this returns true
.
Understanding whether something is passed by value or passed by reference is incredibly important. It'll help prevent a lot of weird bugs down the road, and it's helpful to know when mutating or duplicating pieces of data. Remember, passing by value occurs when dealing with primitive data types, and passing by reference occurs when dealing with arrays and objects.