Can you explain why this line evaluates to true in JavaScript? console.log([] == ![]); // true An empty array…
Can you explain why this line evaluates to true in JavaScript?
console.log([] == ![]); // true
An empty array is equal to the negation of itself?
It looks like a broken language feature, but the JavaScript engine follows strict internal conversion rules.
Here is what happens under the hood during Implicit Type Coercion:
1. Strict Equality (===) vs Loose Equality (==)
• Strict (===): Compares Type + Value. If types differ, it returns false immediately. No conversions occur.
• Loose (==): Compares Value only. If types differ, the engine forces "Implicit Type Coercion" to convert both sides into a common type before comparing.
2. Decoding the Anatomy of [] == ![]
Step 1: Operator Precedence
The Logical NOT (!) operator executes first.
In JavaScript, an array is an object, and all objects are truthy.
Negating a truthy value turns it into boolean false:
![] ➔ false
Now the check becomes:
[] == false
Step 2: The Boolean Coercion Rule
When comparing anything to a Boolean with ==, the engine converts the Boolean to a Number (false becomes 0):
[] == 0
Step 3: The Object-to-Primitive Rule
When comparing an Object/Array to a Number, the engine calls .toString() on the object:
[].toString() produces "" (an empty string).
Now the check becomes:
"" == 0
Step 4: The String-to-Number Rule
When comparing a String to a Number, the engine converts the String to a Number:
Number("") evaluates to 0.
Step 5: The Final Comparison
0 == 0 ➔ true!
3. The Special Case: null and undefined
null == undefined evaluates to true, but:
null == 0 evaluates to false.
Why? The specification explicitly dictates that null and undefined loosely equal each other and nothing else. They do not coerce to numbers under ==.
4. The Production Standard:
This is why modern teams and ESLint enforce the strict equality rule (eqeqeq).
Loose equality (==) introduces silent conversions that hide runtime logic bugs. Always default to strict equality (=== and !==) to keep comparisons explicit and predictable.
Did you know why [] == ![] returned true before seeing the step-by-step trace?