Bitsy Explains
Home/Cursed JavaScript/Lesson 7

Why [] == ![] Is true

Why [] == ![] is true, step by step through what == does to arrays and booleans, and why === makes the curse go away.

Lesson 7 of 103 min readVideo
Video thumbnail: Why [] == ![] Is true Watch the 90-second video
Explain like I'm five

The exclamation mark in front of the array means not. So the line asks whether an empty array equals the opposite of an empty array, and JavaScript says yes. Double equals is like a translator that rewrites both sides as numbers before comparing them, and in translation both sides turn into zero.

The curse

console.log([] == ![]); // prints: true

Something equals its own opposite. To see why, take the line apart one step at a time.

Step by step

Step 1. The not operator runs first. It turns its value into a boolean and flips it. Every array is truthy, even an empty one, so "not an array" is false. The line now reads [] == false.

Step 2. When one side of == is a boolean, JavaScript turns that boolean into a number. false becomes 0. The line now reads [] == 0.

Step 3. When one side is an object (arrays are objects) and the other is a number, the object is turned into a primitive value. An array joins its items into text, and an empty array becomes the empty string "". The line now reads "" == 0.

Step 4. A string compared with a number is turned into a number. "" becomes 0. The line now reads 0 == 0, which is true.

console.log(Boolean([])); // prints: true
console.log(Number(false)); // prints: 0
console.log(Number(String([]))); // prints: 0
console.log("" == 0); // prints: true

This also explains other strange results from the double equals lesson, like [] == false and "" == 0. Both are true for exactly these reasons.

How to break the curse

Use triple equals. === never converts anything. If the two sides are different types, the answer is false straight away.

console.log([] === ![]); // prints: false
console.log("" === 0); // prints: false

An array and a boolean are different types, so there is nothing more to check. No converting, no tricks.

If what you really want to know is whether an array is empty, ask that directly with toys.length === 0. Comparing an array to false or 0 only works by accident.

Bonus twist: [] == [] is false

console.log([] == []); // prints: false
const box = [];
const sameBox = box;
console.log(box === sameBox); // prints: true

When both sides are objects, == and === do the same thing. They check whether both sides are the very same object in memory, as in the copy versus reference lesson. Each [] creates a brand new array, so you get two arrays that look alike, like two identical toys from the same shop. box and sameBox point at one single array, so they are equal.

To compare what is inside two arrays, compare the items. For short lists of simple values, JSON.stringify(a) === JSON.stringify(b) is a quick check.

Rule of thumb

Use ===, because it never converts types, and it works like garlic against JavaScript curses.

Common mistake

Checking whether an array is empty by treating it as a boolean. Arrays are always truthy, so if (toys) runs even for []. Check toys.length === 0 instead.

Check yourself

In [] == ![], what does ![] turn into first?