Why is "0" == [] false?
Truthy and Falsy As well as a type, each value also has an inherent boolean value, generally known as either truthy or falsy. Some of the rules are a little bizarre so understanding the concepts and effect on comparison helps when debugging JavaScript applications.
The following values are always falsy:
- false
- 0 (zero)
- '' or "" (empty string)
- null
- undefined
- NaN (e.g. the result of 1/0)
Everything else is truthy. That includes:
- '0' (a string containing a single zero)
- 'false' (a string containing the text “false”)
- [] (an empty array)
- {} (an empty object)
- function(){} (an “empty” function)
Unexpected situations can occur when comparing truthy and falsy values using the == loose equality:
See the Loose Equality Comparison Table
/*
If Type(x) is Number and Type(y) is String,
return the result of the comparison x == ToNumber(y).
*/
console.log( 0 == '0');
/*
If Type(x) is either String or Number and Type(y) is Object,
return the result of the comparison x == ToPrimitive(y).
*/
console.log( 0 == [] );
/*
If Type(x) is Object and Type(y) is either String or Number,
return the result of the comparison ToPrimitive(x) == y.
*/
console.log( [] == '0');
source: http://es5.github.io/#x11.9.3
JavaScript will try to coerce types when using double equals operator (==
). Exact details in spec: sec 11.9.3: Abstract Equality Comparison Algorithm.
Example 1:
console.log( 0 == '0' ); // true
JavaScript coerces the string to the number 0
, so 0 == 0
.
Example 2:
console.log( 0 == [] ); // true
An empty array's "ToPrimitive" value is zero, when comparing to a number. So this one reduces to 0 == 0
.
Example 3:
console.log( [] == '0' ); // false
console.log( [] == '' ); // true
While the first one looks quite similar to the previous example, neither side is a number. At first glance this appears to be a truthy
comparison, BUT []
is not one of the eight falsy
values.
Demonstrating that []
is truthy, but ''
is falsy:
console.log( [] ? true : false ); // true
console.log( '' ? true : false ); // false
In any sane language, the above two statements would imply that [] == ''
would be false
. But as we saw earlier, that isn't the case in JavaScript!
INEXPLICABLY, []
converts to false
when using ==
:
console.log( [] == true ); // false
console.log( [] == false ); // true
Conclusion: both sides are converted to booleans, but NOT by the "normal" "truthy/falsy" rule (which would convert []
to true
, because it is not one of the eight falsy values). Instead, []
converts to false
when using ==
:
console.log( [] == '0' ); // `false == true`, so false
console.log( [] == '' ); // `false == false`, so true
Example 4:
console.log( [0] == '' ); // false
console.log( [1] == '' ); // false
This could EITHER indicate that the length of the array (1
), which is "truthy", is compared to ''
, which is "falsy", so true == false
, which is false
. OR it could indicate that no type conversion is appropriate, which is case (10) in the spec, so false
. BUT see example 5, which doesn't seem to fit either possibility.
Example 5:
console.log( [0] == '0' ); // true
console.log( [0] == '1' ); // false
console.log( [1] == '1' ); // true
console.log( [1] == '0' ); // false
console.log( [2] == '2' ); // true
console.log( [1] == '2' ); // false
Surprisingly, an array that contains one number, apparently is converted to that number, when comparing to a non-empty string. This then goes to the rule for comparing a number to string, which converts the string to a number. So we are comparing either (0
or 1
or ..) to (0
or 1
or ..).
Is this an ambiguity in the spec? Differs between different implementations?
Testing done at https://www.webtoolkitonline.com/javascript-tester.html
on 2020-Oct-27, in Chrome on a Windows 10 pc, using alert
instead of console.log
.
Example 6:
If all the above doesn't convince you to never use ==
again, consider this:
var a = [];
console.log( a == a ); // true
BUT:
console.log( [] == [] ); // false
None of the 9 type conversion rules in the spec apply, so this is case 10: they aren't the same object, even though neither of them has any contents. They are two different instances of an empty array.
In all, this is why its generally safer to use triple equals, which checks type and equality.
A handy illustration (for the cases that test truthiness) below:
function truthyOrFalsy(val) {
return val ? "Truthy" : "Falsy";
}
console.log("empty array:", truthyOrFalsy([]));
console.log("number zero:", truthyOrFalsy(0));
console.log("string with a zero character:", truthyOrFalsy("0"));