In JavaScript, two objects may not be equal even though they appear to be similar. Why is that the case? ๐ค Let's understand why.
For example:
const obj1 = {
name: "Dillion"
}
const obj2 = {
name: "Dillion"
}
console.log(obj1 === obj2)
// false
As you can see here, obj1
and obj2
look similar. They both have the property of name
with a value of "Dillion". But comparing them--obj1 === obj2
--returns false
. ๐ค
The same thing applies to arrays:
let arr1 = [1, 2, 3]
let arr2 = [1, 2, 3]
console.log(arr1 === arr2)
// false
To understand why this is the case, you have to understand what primitive and reference values are in JavaScript.
Primitive and Reference Values
Think of a primitive value as one value (static, fixed) and a reference value as a group of multiple values or (dynamic) value.
Primitive values are of the types string
, number
, boolean
, null
, undefined
, symbol
, and BigInt
. These values are fixed and stored on the stack, for example:
let name = "Dillion"
let age = 60
let isRaining = true
Reference values are of the object
types which includes objects, arrays and functions. These values are dynamic (can contain multiple values, properties, and can be modified over time) and are stored on the heap, with a reference value in the stack, for example:
let array = [1, 2, 3]
let obj = { name: "Dillion" }
function print() {
console.log('hello')
}
The reference value is an address that points to the location of the data in the memory.
Here's an article where I explained the difference in more detail: Primitive and Reference Values Simplified
Comparing Primitive and Reference Values
When you compare primitive values, you are comparing static values, which have a fixed size on the stack:
let name = "Dillion"
let name2 = "Dillion"
console.log(name === name2)
// true
Here, we compare name
and name2
if they are equal. What happens here is that JavaScript checks for the name
and name2
variables in the stack, and then it sees that they have equal values, so it's true--they are equal.
In the case of objects, you are comparing the references (the addresses) and not the exact values. Here's what I mean.
If you have two arrays like this:
let array = [1, 2, 3]
let array2 = [1, 2, 3]
Here's what it would look like on the stack and heap:
As you can see here, for array
, [1, 2, 3]
is not stored on the stack. It is stored on the heap, and the memory location of that data is stored on the stack as a reference.
The same thing for array2
; [1, 2, 3]
is not stored on the stack. It is stored on the heap, in a different memory location and the reference is stored on the stack.
When you compare both arrays like array === array2
, you're not exactly comparing [1, 2, 3] === [1, 2, 3]
but you're actually comparing refForArray === refForArray2
(ref is short for reference).
As we saw in the heap illustration, array
and array2
have different memory locations, which means they have different references, which then means the variable array
is not equal to the variable array2
.
The only way array
and array2
can be equal, is if you have something like:
let array = [1, 2, 3]
let array2 = array
console.log(array === array2)
// true
By assigning array
to array2
, you are assigning the reference that array
holds in the stack, to array2
:
Therefore, array
and array2
now have the same value--the same reference.
How to compare objects
We've seen that in our attempt to compare two object values, we are actually comparing the reference and not the object data. So how do we correctly compare the object data?
There are a couple of ways you can do this but I will share two of them.
Compare objects using _.isEqual
from Lodash
You can write a function that does an equality check between two objects, but it could become complicated when you have to compare deeply nested objects which can have different values, including objects.
A faster approach is using the isEqual method from Lodash which is an effective solution that handles deep comparison between two values:
const _ = require('lodash');
const array = [1, 2, 3]
const object = { name: "Dillion" }
const array2 = [1, 2, 3]
const object2 = { name: "Dillion" }
console.log(_.isEqual(array, array2))
// true
console.log(_.isEqual(object, object2))
/ true
Compare objects using JSON.stringify()
Say you don't want to use Lodash, you can use JSON.stringify
which recursively stringifies an object or array to a string:
const array = [1, 2, 3]
const object = { name: "Dillion" }
const array2 = [1, 2, 3]
const object2 = { name: "Dillion" }
console.log(
JSON.stringify(array)
===
JSON.stringify(array2)
)
// true
console.log(
JSON.stringify(object)
===
JSON.stringify(object2)
)
// true
Since the stringified version is a primitive type (static), the data of both values can be compared. But here is a limitation with JSON.stringify()
.
JSON.stringify
can return different results if the order or properties in an object are different. For example:
const object = {
name: "Dillion",
age: 50
}
const object2 = {
age: 50,
name: "Dillion"
}
console.log(
JSON.stringify(object)
===
JSON.stringify(object2)
)
// false
In object
, we have name
coming before age
, but in object2
, we have age
coming before name
. This means their stringified representations would be different and as a result, their data are not equal.
Wrap up
Primitive and Reference values are fundamental concepts to understand when working with data in JavaScript. And as we have seen in this article, comparing primitive values is easier, but comparing reference values can be trickier because when you think you are comparing the data, you may not realize that you are actually comparing the reference.
I am currently building a newsletter to share informative articles and news, covering ai, startups ๐, tech ๐ฑ, and programming ๐ป! my progress in becoming Software Developer with little experience, and how you can learn from my experience.
Please subscribe via this link.
Also published here.
I hope this article answers the question of "Why are two similar objects not equal in JavaScript?". If it does, please give this article a share ๐๐พ