From what I know, the if
statement in JavaScript casts the result of its condition to a Boolean, and then executes it like the following:
if(true) {
// run this
}
if(false) {
// do not run this
}
And that works. But If I do this:
if('0' == false) {
// We get here, so '0' is a falsy value
}
Then I would expect this:
if('0') {
// We don't get here, because '0' is falsy value
}
But instead, I get:
if('0') {
// We *DO* get here, even though '0' is falsy value
}
What's happening? Apparently, if
does not check if its condition is a truthy or falsy value, but does some other conversion?
From what I know, the if
statement in JavaScript casts the result of its condition to a Boolean, and then executes it like the following:
if(true) {
// run this
}
if(false) {
// do not run this
}
And that works. But If I do this:
if('0' == false) {
// We get here, so '0' is a falsy value
}
Then I would expect this:
if('0') {
// We don't get here, because '0' is falsy value
}
But instead, I get:
if('0') {
// We *DO* get here, even though '0' is falsy value
}
What's happening? Apparently, if
does not check if its condition is a truthy or falsy value, but does some other conversion?
-
1
'0'
is not a false-y value. See the specification on why the equality works as it does (there are additional coercion rules not in[ToBoolean]
). – user2246674 Commented May 8, 2013 at 21:48 -
Possibly because
'0'
is aString
(and not aboolean
itself). In order to'0' == false
evaluate to true, the engine probably does some conversion before actually evaluating the expression. – acdcjunior Commented May 8, 2013 at 21:49 - stackoverflow./questions/5659085/… – gongzhitaao Commented May 8, 2013 at 21:50
- I think if(null) { } will also not execute in JS. Probably has something to do with the old C++ world where "anything that isn't 0 is true" – K0D4 Commented May 8, 2013 at 21:50
- exact duplicate of Why do alert(!!"0") and alert(false == "0") both output true in JavaScript – Bergi Commented May 8, 2013 at 22:40
4 Answers
Reset to default 7This is just one of those "gotchas" with the ==
rules which are rather plex.
The parison x == y, where x and y are values, produces true or false. Such a parison is performed as follows:
(4) If Type(x) is Number and Type(y) is String, return the result of the parison x == ToNumber(y).
(5) If Type(x) is String and Type(y) is Number, return the result of the parison ToNumber(x) == y.
(6) If Type(x) is Boolean, return the result of the parison ToNumber(x) == y.
(7) If Type(y) is Boolean, return the result of the parison x == ToNumber(y).
In this case, that means that '0' == false
is first coerced to '0' == 0
(by rule #7) and then, on the second pass through, it is coerced to 0 == 0
(by rule #5) which results in true.
This particular case is somewhat tricky because of false ~> 0
instead of '0' ~> true
(as what might be expected). However, '0'
is itself a truth-y value and the behavior can be explained with the above rules. To have strict truthy-falsey equality in the test (which is different than a strict-equality) without implicit conversions during the equality, consider:
!!'0' == !!false
(For all values: !falsey -> true
and !truthy -> false
.)
This:
if('0') {
// We *DO* get here, even though '0' is falsy value
}
checks to see if the string is null or empty, not whether it's zero or not. Any non-empty string is truthy.
When you do this:
if('0' == false) {
// We get here, so '0' is a falsy value
}
you are asking the JS engine for an explicit type conversion to try to match the type of the two operands. That is different than just asking if one operand is truthy all by itself.
In general, you will find that you get fewer unexpected results if you nearly always use ===
and !==
and only allow type coercion when you know exactly what is going to happen in all cases either because you fully understand the very plex coercion rules or because you know what types will be present and you understand those specific cases.
if ('0' == false)
:
Javascript is doing something called type coercion
.
Following the rules in that link, we fall to rule 7:
If Type(y) is Boolean, return the result of the parison x == ToNumber(y)
Calling ToNumber(false) gives us a numeric 0. The result now starts to make sense, but we're still not quite done, because we still have a string and a number. The process starts again, and this time we fall to rule 5:
If Type(x) is String and Type(y) is Number, return the result of the parison ToNumber(x) == y: "2" == 2
This time, the left side '0' is converted to a number: 0. Now, at last, we can pare two Numbers, and since 0 equals 0, the result is true. However, it's important to note that this implies nothing at all about the truish/falsy nature of the '0'
string, because it was coerced before it was pared.
if('0')
In this case, there is no parison; you only want to know if a single value is "truish" or "falsy". No type coercion is used, because strings can be evaluated as truish or falsy on their own merits. Using the rules at the same link as before, we find this information:
In JavaScript, and not only JavaScript, we have so called falsy values. These are respectively: 0, null, undefined, false, "", NaN. Please note the empty string is empty, 'cause differently from php as example, "0" will be considered truish
The quote is especially helpful because it specifically calls out the '0' string, but that would not be necessary. It's enough to know that an empty string is falsy, and any other string is truish, because the content of the string is not evaluated and no coercion is performed. 0
may be a falsy value, but because we evaluate a string rather than coercing to a number, and '0'
has a value of some kind, it is still truish.
Javascript operator ==
does type conversion and is basically useless. Just avoid it.
For example:
[]
is truty but[] == false
is true1 == "1"
,[1] == "1"
,[[1]] == "1"
are all true[1] == [[1]]
however is false
The rules are VERY weird. For example in the first case []
gets converted to ""
that gets converted to a number and the value is 0
. false
is also converted to the number 0
. So in the end they pare equal.
Note however that while the conversion from the empty string to a number gives 0
, the result of parseInt("")
is NaN
.
PS: The real fun is when you discover that [30,20,10,3,2,1].sort()
returns [1,10,2,20,3,30]
(yes... numbers, yes in lexicographical order). No I'm not kidding.