tl;dr: Converting a value to Boolean is very different from comparing a value to Boolean because JavaScript has implicit type conversion.
Can someone explain to me what is going on here?
!x
is the same as doing !Boolean(x)
, i.e. x
is explicitly converted to a Boolean.
With loose comparison, things are a little more evolved:
If the two operands are not of the same data type, JS will convert both of them to the same type. However, if any of the operands is a Boolean, that value is converted to a number, independently of the value of the other operand. That in turn means that the other operand is converted to a number as well.
So [] == false
is the same as Number([]) == Number(false)
.
The algorithm for loose comparison can be found in the ECMAScript spec.
See also Why does {} == false evaluate to false while [] == false evaluates to true?, where I listed the conversion steps.
I assumed using ! would coerce the type correctly similar to how it works in PHP.
While there are certainly some similarities, the type conversion rules are complete independent.
Opinionated advice: This is a good example of why implicit type conversion should be avoided if possible.