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) is Boolean, return the result of the comparison ToNumber(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) is Number and Type(y) is String, return the result of the comparison x == 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.