It's because !=
does implicit type conversion. If you used the strict version, !==
, it would do what you expect. But the loose version, !=
, will convert both of those operands to numbers, and both ""
and false
convert to 0
, so "" != false
is false, because it ends up (through a series of convolutions) being 0 != 0
.
This is laid out in detail in Abstract Equality Comparison algorithm in the specification:
- ReturnIfAbrupt(x).
- ReturnIfAbrupt(y).
- If Type(x) is the same as Type(y), then
Return the result of performing Strict Equality Comparison x === y.
- If x is null and y is undefined, return true.
- If x is undefined and y is null, return true.
- If Type(x) is Number and Type(y) is String,
return the result of the comparison x == ToNumber(y).
- If Type(x) is String and Type(y) is Number,
return the result of the comparison ToNumber(x) == y.
- If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
- If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
- If Type(x) is either String, Number, or Symbol and Type(y) is Object, then
return the result of the comparison x == ToPrimitive(y).
- If Type(x) is Object and Type(y) is either String, Number, or Symbol, then
return the result of the comparison ToPrimitive(x) == y.
- Return false.
As we can see from the above, if we start out with false
and ""
, then:
- We follow Step 8, convert
false
to 0
, and start again with 0 != ""
- We follow Step 6, convert
""
to 0
, and start again with 0 != 0
- We follow Step 3 and get the result
false
(because we're doing !=
, whereas the algorithm is defined in terms of ==
).