It’s hard to believe.
How can an Array not be an Array?
It makes no sense — []
is truthy and ![]
should be false
.
So how can []
equal false?
And this somehow doesn’t happen for other types like strings and numbers:
Are JavaScript arrays broken?
What happened here
Dump all the blame on the dangerous ==
operator.
This is just another instance of why we always tell new JavaScript devs to NEVER use it (ever).
Especially if they’ve already been coding with stubbornly strict languages like C#.
At first glance, it doesn’t seem like there’s anything wrong with ==
:
// C#
int num = 2;
Console.WriteLine(num == 10); // false
Console.WriteLine(num == 2); // true
// JS
let num = 2;
console.log(num == 10); // false
console.log(num == 2); // true
But now look what happens here:
// C#
int num = 2;
// ❌ Error: can't compare int and string
Console.WriteLine(num == "2"); // false
But look what happens in JavaScript:
// JS
let num = 2;
console.log(num == "2"); // true?
JavaScript auto-casts the string into a number!
This is one of the many frustrations people have with JavaScript that made TypeScript come along.
Instead of just failing and stopping us from doing dumb stuff, it just goes “Mhmm okay, if you say so…”
// Huh?
console.log(null == undefined); // true
// Converts array to string
console.log([1,2,3] == "1,2,3"); // true (seriously?)
So what do you think REALLY happens in [] == ![]
, behind the scenes?
First of all, empty arrays are truthy in JavaScript, so !
acts on it to make it false
[] == false
So now surprise surprise, we suddenly find ourselves comparing an Array
to Boolean
. Obviously not gonna end well.
As we now know, JS doesn’t care so it just goes ahead — this time casting the Boolean
to the equivalent number
[] == Number(false)
[] == 0
Next, thanks to some garbage rules that you don’t need to ever know, []
turns into… an empty string?
// don't ask
"" == 0
And now finally it converts ""
into… a Number:
0 == 0 // true
So what’s the solution to avoid this nonsense?
Always use the strict equality operator ===
(and I mean always).
// font ligatures make triple equal look nicer
console.log(2 == '2'); // ❌ true
console.log(2 === '2'); // ✅ false
console.log([] == ![]); // ❌ true
console.log([] === ![]); // ✅ false
There is no scenario imaginable in the universe where ==
can be used that ===
can’t be
And now with ===
, your lovely VS Code editor suddenly comes alive to stop us from doing something like this:
But before it was asleep:
But what about [] == []
?
Okay this makes sense, but what then could possibly explain this:
Surely the ==
can’t be blamed here. They have the same type, don’t they?
Yes they do.
The problem is that JavaScript compares arrays by reference. Not by value.
They may have exactly the same value, but as long they don’t refer the same object in memory, they will never be equal in the eyes of ==
and ===
// these are creating new array objects
// on the fly, stored at different locations
console.log([] == []); // false
console.log([] === []); // false
// store new object this time
const arr = [];
// reuse it
const tari = arr;
// both vars refer to the same object in memory
console.log(arr == tari); // true
console.log(arr === tari); // true
And this is how it is for objects in general too:
console.log({} === {}); // false
const person1 = { name: 'tari ibaba' };
const person2 = { name: 'tari ibaba' };
const person3 = person1;
// Only person1 and person3 point to the same object
console.log(person1 === person2); // false
console.log(person1 === person3); // true
console.log(person2 === person3); // false
But of course, this isn’t the case for our core primitive values — strings, numbers, and booleans:
console.log('tari ibaba' === 'tari ibaba');
// As we saw previously
console.log(2 === 2);
So what do you do when you want to compare arrays by their element values?
If it’s sorted you can use JSON.stringify()
:
function arraysEqual(array1, array2) {
return JSON.stringify(array1) === JSON.stringify(array2);
}
Otherwise, you go for the more generic length
and every()
combo:
function arraysEqual(array1, array2) {
return (
array1.length === array2.length &&
array1.every((value, index) => value === array2[index])
);
}
Final thoughts
==
is just one example of JavaScript’s looseness making it do things that make no sense in the real world.
Moral lesson: Always use strict equality, use TypeScript, and prioritize modern features.
Every Crazy Thing JavaScript Does
A captivating guide to the subtle caveats and lesser-known parts of JavaScript.