Why does [] == ![] return TRUE in JavaScript?

Last updated on September 01, 2024
Why does [] == ![] return TRUE in JavaScript?

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:

It still compiles though -- unless you use TypeScript

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.

Coding Beauty Assistant logo

Try Coding Beauty AI Assistant for VS Code

Meet the new intelligent assistant: tailored to optimize your work efficiency with lightning-fast code completions, intuitive AI chat + web search, reliable human expert help, and more.

See also