Comparison by value vs reference

Not every type in JavaScript is compared in the same way. When we check if two strings are equal, their value will be compared. However, objects and arrays are compared by reference. In this post I will briefly discuss the difference between the two ways of comparison.

When writing conditionals in our daily development work, we often need to check if two values are equal. If they are, we’ll do something; otherwise something else will be executed. For example:

const id = 256;

if (id === 256) {
  console.log('This item has an id of 256.');
} else {
  console.log('Unknown id.')
}

We will see the “This item has an id of 256.” message in the console when running the code, so really no surprise here.

But the case is not that simple when we need to compare more complex instruments like objects or arrays. Let’s discuss then the difference between comparing by value vs reference.

Comparing by value

Number and string types are compared by value.

Numbers

When a variable is declared and it’s assigned a number, it will “book” its dedicated spot in the memory. Consider the following assignment:

const firstNumber = 5;

Imagine that all numbers JavaScript can handle are wired to an imaginary lightbulb on a long shelf. When a variable is assigned a number, the corresponding bulb will be turned on. One lightbulb always belongs to one and only one number. As a consequence number 6 is wired to a different lightbulb from number 5, number 128.62 is connected to another bulb again and so on.

Now let’s create a second variable and assign it the same number:

const secondNumber = 5;
console.log(firstNumber === secondNumber); // true

The equality will be true, because both firstNumber and secondNumber variables is connected to the same lightbulb, i.e. the same spot in memory, which belongs to 5. This is why the following statements will always return true:

const thirdNumber = firstNumber;
console.log(thirdNumber === firstNumber); // true
console.log(thirdNumber === secondNumber); // true

We make thirdNumber equal to firstNumber and don’t explicitly assign 5 to it. But the type of firstNumber is number, and numbers are compared by value.

As a result firstNumber, secondNumber and thirdNumber are all wired to the very same imaginary lightbulb, which belongs to 5, so they will prove to be equal.

This is called comparison by value.

Strings

Strings behave similarly to numbers:

const firstTree = 'tree';
const secondTree = 'tree';
console.log(firstTree === secondTree);
const thirdTree = firstTree;
console.log(thirdTree === firstTree);
console.log(thirdTree === secondTree);

The string tree receives a dedicated place in the memory and however a variable is referred to the word tree (either by direct assignment or referencing to a variable), as long as the strings are the exact match, the equalities will always return true.

Quite a few lightbulbs are needed to cover all numbers and strings!

Comparing by reference

Objects and arrays behave differently as they are compared by reference.

Objects

This means that each object will create their own space in memory when they get declared, regardless of their keys and values.

Consider the following object:

const obj1 = { a: 1, b: 2 };

{ a: 1, b: 2 } will turn on its own bulb and the obj1 variable will be wired to it. If we create an obj2 variable and make it equal to obj1, the equality will be true:

const obj2 = obj1;
console.log(obj1 === obj2); // true

Why?

Because obj2 refers to obj1 and obj1 refers to the spot dedicated to the { a: 1, b: 2 } object in memory. In terms of bulbs, obj2 is connected to obj1 and obj1 is wired to the lightbulb of { a: 1, b: 2 }. So far it seems to be the same scenario as above.

Now what happens when we create an obj3 variable and assign { a: 1, b: 2 } to it?

const obj3 = { a: 1, b: 2 };
console.log(obj1 === obj3); // false

The equality check returns false, which means they are not equal!

The reason is that obj3 is assigned to another object, { a: 1, b: 2 }. Even if both obj1 and obj3 have the same keys and values, each object literal will create it’s own space in memory when assigned to a variable. This means that we now have two bulbs, one for each { a: 1, b: 2 }.

When an equality check occurs with === (or even with ==), it’s like checking if the variables are wired to the same lightbulb.

In the case of obj1 and obj2, they are. obj2 is made equal to obj1 and therefore both refer to the same bulb. (Yeah, I’ll keep using the bulb theme here. Feel free to change it to something else.)

However, we assigned a new object to obj3, even if that object contains the same properties and values as obj1’s. New object literals create their own spot in memory (i.e. they will be wired to a different lightbulb), hence obj3 will not be equal to either obj1 or obj2.

It’s no surprise now that any reference to obj3 will make the equality statement return true:

const obj4 = obj3;
console.log(obj4 === obj3); // true
console.log(obj4 === obj2); // false

On the contrary obj2 has a reference to the { a: 1, b: 2} of obj1, so the last statement will return false.

Or even easier:

console.log({ a: 1, b: 2 } === { a: 1, b: 2 }); // false
console.log({ a: 1, b: 2 } == { a: 1, b: 2 }); // false

It’s definitely not about using === over ==!

Arrays

Arrays behave the same way as objects:

const arr1 = [2, 'tree'];
const arr2 = [2, 'tree'];
const arr3 = arr2;

console.log(arr2 === arr3); // true
console.log(arr1 === arr2); // false

New arrays also demand their own spot it memory, and they are also compared by reference.

Conclusion

To sum up, strings and numbers are compared by value, but objects and arrays are compared by reference.

New object literals and arrays create their own place in memory, hence they won’t be equal even if they seem to be the exact copy of each other.

On the other hand, strings and numbers are compared by value and they refer to the same place in memory.

Thanks for reading and see you next time.