JavaScript compare two objects. Why is that case two objects not equal even though they are similar?
In JavaScript, two objects cannot be equal even though they are 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
andobj2
look similar; they both have the property ofname
with a value of "Dillion". But comparing them--obj1 === obj2
--returnsfalse
????
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.
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 typesstring
,number
,boolean
,null
,undefined
,symbol
, andBigInt
. These values are fixed and stored on the stack, for example:
let name = "Dillion"
let age = 60
let isRaining = true
Reference values are of theobject
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
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 comparename
andname2
if they are equal. What happens here is that JavaScript checks for thename
andname2
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, forarray
,[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 forarray2
;[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 likearray === array2
, you're not exactly comparing[1, 2, 3] === [1, 2, 3]
but you're actually comparingrefForArray === refForArray2
(ref is short for reference).
As we saw in the heap illustration,array
andarray2
have different memory locations, which means they have different references, which then means the variablearray
is not equal to the variablearray2
.
The only wayarray
andarray2
can be equal, is if you have something like:
let array = [1, 2, 3]
let array2 = array
console.log(array === array2)
// true
By assigningarray
toarray2
, you are assigning the reference thatarray
holds in the stack, toarray2
:
Therefore,array
andarray2
now have the same value-the same reference.
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.
_.isEqual
from LodashYou 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
JSON.stringify()
Say you don't want to use Lodash, you can useJSON.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 withJSON.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
Inobject
, we havename
coming beforeage
, but inobject2
, we haveage
coming beforename
. This means their stringified representations would be different and as a result, their data are not equal.
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.