Why does !!1=="1" equal true and !!2=="2" equal false?
As the title states, why does:
> !!1=="1"
equal
True
and
> !!2=="2"
equal:
False
Likewise, why does > "1"==true
equal true
and > "2"==true
equal false
I'm baffled. Are these just bugs in JS or what's going on here?
As per the Operator precedence rules, logical !
has higher priority over ==
. So, in both the cases, !!
is evaluated first.
Note: Truthiness of various objects have been explained in this answer of mine.
First Case
!!1 == "1"
!1
will be evaluated to false
, since 1
is considered Truthy. Negating again we get true
. So the expression becomes
true == "1"
Now, the coercion rules kick in as you have used ==
operator, which evaluates as per the The Abstract Equality Comparison Algorithm defined in ECMAScript 5.1 Specification,
6. If
Type(x)
isBoolean
, return the result of the comparisonToNumber(x) == y
.
So, true
will be converted to a number, which is 1 as per ToNumber
algorithm for Boolean values. Now the expression becomes
1 == "1"
Now,
4. If
Type(x)
isNumber
andType(y)
isString
, return the result of the comparisonx == ToNumber(y)
.
So, "1"
will be converted to a number and that will give 1, as per the ToNumber
algorithm. That is why it shows true
in the first case.
Second Case
The same rules are applied here.
!!2 == "2"
becomes
true == "2"
then
1 == "2"
which becomes
1 == 2
which is not true
, that is why the second case prints false
.
tldr; this is due to the [ToNumber] conversions in the ==
operator algorithm.
The first step is to simplify the expression. Since !!x=="x"
is parsed like (!!x)=="x"
and !!a_truthy_expression -> true
, the actual relevant expression for the equality is
!!1=="2" -> true=="1" -> Boolean==String
!!2=="2" -> true=="2" -> Boolean==String
So then looking at the rules for 11.9.3 The Abstract Equality Comparison Algorithm and following along with the application yields
Rule 6 - If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
which results in Number==String
or 1=="1" and 1=="2", respectively1. Then the rule
Rule 7 - If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
is applied which results in Number==Number
or 1==1 and 1==2, respectively1; the latter is clearly false.
Rule 1 - If Type(x) is the same as Type(y), then [by c.iii.] If x is the same Number value as y, return true [else return false].
(The same algorithm explains the String==Boolean
case when the complementing rules are applied.)
1To see the [ToNumber] rule applied, consider:
+false -> 0
+true -> 1
+"1" -> 1
+"2" -> 2
Its a precedence operator problem.
The !
operator is an unary operator. That means the left side must be an expression or a boolean evaluable section. See Javascript MDN.
!!1==1 is not necessary !!(1==1)
!!2==2 is not necessary !!(2==2)
I think that these expressions should be consistent if the equal operator has more precedence than ! operator. But if we consider the opposite, evaluating first negations we have:
!!1 == 1
!1 -> false
!!1 -> true
!!1 == 1
And with the two
!!2==2
!2 -> false
!!2 -> true
(!!2) == 2 -> false
That is because the ! operator has precedence over == operator
See Mozilla Operator Preference
!!1
is equal to true, and "1" is equal to true ("0" is false, so is every other string). So !!1 == "1"
evaluates to true == true
, which of course returns true.
!!2
is also equal to true. As I mentioned earlier, "2" is not "1", so it's false. Therefore, we have true == false
, which of course returns false.
If you want to see if 2 (a number) is equal to "2" (a string representation of a number), then all you have to do is 2 == "2"
, which evaluates to 2 == 2
, which is true. The difference is that we're not comparing a boolean against a boolean. We're comparing a number against a number.
Basically, putting !!
in front of a number converts to a boolean, which forces JavaScript to cast your string to a boolean instead of a number.