Why does !!1=="1" equal true and !!2=="2" equal false? - javascript

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.

Because "1" may be considered as "true" when you do equality check, not identity, but "2" - can't.

Related

What is happening in this loose equality comparison of 2 empty arrays

I am struggling with understanding how this snippet works on a basic level
if([] == ![]){
console.log("this evaluates to true");
}
Please help me understand where I got it wrong. My thinking:
First there is operator precedence so ! evaluates before ==.
Next ToPrimitive is called and [] converts to empty string.
! operator notices that it needs to convert "" into boolean so it takes that value and makes it into false then negates into true.
== prefers to compare numbers so in my thinking true makes 1 and [] is converted into "" and then 0
Why does it work then? Where did I get it wrong?
Why does it work then?
TLDR:
[] == ![]
//toBoolean [1]
[] == !true
[] == false
//loose equality round one [2]
//toPrimitive([]); toNumber(false) [3]
"" == 0
//loose equality round two
//toNumber("") [4]
0 === 0
true
Some explanations:
1)
First there is operator precedence so ! evaluates before ==
Negating something calls the internal toBoolean method onto that "something" first. In this case this is an Object (as Arrays are Objects) and for that it always returns true which is then negated.
2)
Now it's up to loose equalities special behaviour ( see Taurus answer for more info) :
If A is an Object ( Arrays are Objects ) and B is a Boolean it will do:
ToPrimitive(A) == ToNumber(B)
3)
toPrimitive([])
ToPrimitive(A) attempts to convert its Object argument to a primitive value, by attempting to invoke varying sequences of A.toString and A.valueOf methods on A.
Converting an Array to its primitive is done by calling toString ( as they don't have a valueOf method) which is basically join(",").
toNumber(false)
The result is 1 if the argument is true. The result is +0 if the argument is false. Reference
So false is converted to +0
4)
toNumber("")
A StringNumericLiteral that is empty or contains only white space is converted to +0.
So finally "" is converted to +0
Where did I get it wrong?
At step 1. Negating something does not call toPrimitive but toBoolean ...
The accepted answer is not correct (it is now, though), see this example:
if([5] == true) {
console.log("hello");
}
If everything is indeed processed as the accepted answer states, then [5] == true should have evaluated to true as the array [5] will be converted to its string counterpart ("5"), and the string "5" is truthy (Boolean("5") === true is true), so true == true must be true.
But this is clearly not the case because the conditional does not evaluate to true.
So, what's actually happening is:
1. ![] will convert its operand to a boolean and then flip that boolean value, every object is truthy, so ![] is going to evaluate to false.
At this point, the comparison becomes [] == false
2. What gets into play then is these 2 rules, clearly stated in #6 in the specs for the Abstract Equality Comparison algorithm:
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)
At this point, the comparison becomes [] == 0.
3. Then, it is this rule:
If Type(x) is Object and Type(y) is either String or Number,
return the result of the comparison ToPrimitive(x) == y.
As #Jonas_W stated, an array's ToPrimitive will call its toString, which will return a comma-separated list of its contents (I am oversimplifying).
At this point, the comparison becomes "" == 0.
4. And finally (well, almost), this rule:
If Type(x) is String and Type(y) is Number,
return the result of the comparison ToNumber(x) == y.
An empty string converted to a number is 0 (Number("") == 0 is true).
At this point, the comparison becomes 0 == 0.
5. At last, this rule will apply:
If Type(x) is the same as Type(y), then
.........
If Type(x) is Number, then
.........
If x is the same Number value as y, return true.
And, this is why the comparison evaluates to true. You can also apply these rules to my first example to see why it doesn't evaluate to true.
All the rules I quoted above are clearly stated here in the specs.
First, realize that you are comparing two different types of values.
When you use ![] it results in false. You have code (at core it results in this):
if([] == false)
Now the second part: since both values are of different type and you are using "==", javascript converts both value type into string (to make sure that they have same type). It starts from left.
On the left we have []. So javascript applies the toString function on [], which results in false. Try console.log([].toString())
You should get false on the left as string value. On the right we have boolean value false, and javascript does the same thing with it.
It use the toString function on false as well, which results in string value false.
Try console.log(false.toString())
This involves two core concepts: how "==" works and associativity of "==" operator - whether it starts from left or right.
Associativity refers to what operator function gets called in: left-to-right or right-to-left.
Operators like == or ! or + are functions that use prefix notation!
The above if statement will result in false if you use "===".
I hope that helps.

confusing behavior of javascript [duplicate]

Given the following code:
if ("string") {
console.log('true!');
}
//logs "true" to the console
if ("string"==true) {
console.log('true!');
}
//doesn't log anything
Why does this happen? I thought "string" was being cast to a number, as is the boolean. So true becomes 1, and "string" becomes NaN. The second if statement makes sense, but I don't see why the first statement causes the inner loop to be evaluated. What's going on here?
It is being cast to Boolean. Any non-empty string evaluates to true.
From the ECMAScript Language Specification:
12.5 The if statement
Semantics
The production IfStatement: if ( Expression ) Statement else Statement is evaluated as follows:
Let exprRef be the result of evaluating Expression.
If ToBoolean(GetValue(exprRef)) is true, then
Return the result of evaluating the first Statement.
Else,
Return the result of evaluating the second Statement.
9.2 ToBoolean
The abstract operation ToBoolean converts its argument to a value of type Boolean according to Table 11:
Table 11 - ToBoolean Conversions
Undefined: false
Null: false
Boolean: The result equals the input argument (no conversion).
Number: The result is false if the argument is +0, -0, or NaN; otherwise the result is true.
String: The result is false if the argument is the empty String (its length is zero);
otherwise the result is true.
Object: true
As far as the == operator is concerned, it's complicated, but the gist of it is that if you compare a number to a non-number the latter is converted into a number. If you compare a boolean against a non-boolean, the boolean is first converted to a number, and then the previous sentence applies.
See section 11.9.3 for details.
// Call this x == y.
if ("string" == true)
// Rule 6: If Type(y) is Boolean,
// return the result of the comparison x == ToNumber(y).
if ("string" == Number(true))
// Rule 5: If Type(x) is String and Type(y) is Number,
// return the result of the comparison ToNumber(x) == y.
if (Number("string") == Number(true))
// The above is equivalent to:
if (NaN == 1)
// And NaN compared to *anything* is false, so the end result is:
if (false)
Non-empty strings are truthy, but are not necessarily equivalent to true.
== is a "soft" equality operator.
It uses type coercion to compare two equivalent objects as equal.
All of the following are true:
42 == "42"
0 == false
0 == ""
[] == ""
{} == "[object Object]"
"1" == true
Aribtrary strings are not equivlant to any primitive values.
However
When you write if (something), the if will execute if something is "truthy".
All values are truthful except the following:
false
0
NaN
""
null
undefined
if ("string"===true)
Should be written this way.
"string" is a string which is not null. In JavaScript everything not being null evaluates "true". So: if("string") is the same as if("string" != null) but "string" is not true, it is still a string value.
I think this is happening because in the first example, your "string" is a non-null object, which translates to true in this context, whereas in the second example, you're asking if this String object is the same as the Boolean object, which it's not, so it translates to false.
if ("string") {
console.log('true!');
}
As you may already know, if evaluates a boolean expression. So it checks
if((Boolean)"string")
Since (bool)string is true it passes. But in the case of
if ("string"==true) {
console.log('true!');
}
You are trying to equate a string with a bool, which obviously compares them and returns false.
Simple:
if("string") is evaluated as a boolean. Any value that isn't false is true, no conversion to number or anything of that sort.
Comparing "string" to a boolean value true will obviously yield false.
From the ECMA 262 reference, if you convert implicitly a String to Boolean, and the String is other than the empty String, it will evaluate to true.
Check here

Why is type coercion not occuring here? [duplicate]

MDC describes the == operator as follows:
If the two operands are not of the same type, JavaScript converts the operands then applies strict comparison. If either operand is a number or a boolean, the operands are converted to numbers if possible; else if either operand is a string, the other operand is converted to a string if possible.
With this in mind, I would evaluate "true" == true as follows:
Are they of the same type? No
Is either operand a number or boolean? Yes
Can we convert both to a number? No (isNaN(Number("true")) // true)
Is either operand a string? Yes
Can we convert the other operand to a string? Yes (String(true) === "true" // true)
I've ended up with the strings "true" and "true", which should evaluate to true, but JavaScript shows false.
What have I missed?
Because "true" is converted to NaN, while true is converted to 1. So they differ.
Like you reported, both are converted to numbers, because at least true can be (see Erik Reppen's comment), and then compared.
The == comparison operator is defined in ECMA 5 as:
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).
So, "true" == true is evaluated as:
"true" == ToNumber(true) (via rule 7)
"true" == 1
ToNumber("true") == 1 (via rule 5)
NaN == 1
===> false
Acording to The Abstract Equality Comparison Algorithm
http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3
if one of the oprends is a boolean and other is not, boolean is converter to number 0 or 1. so true == "true" is false.
The equality operators (== and !=) use the Abstract Equality Comparison Algorithm to compare two operands.
"true" == true
Since "true" is String and true is Boolean, we need to return the result of "true" == Number(true) (step 7 in the algorithm), which is "true" == 1.
"true" == 1
Since "true" is String and 1 is Number, we need to return the result of Number("true") == 1 (step 5 in the algorithm). Number("true") returns NaN.
Now we have NaN == 1.
NaN == 1
Now both operands are of the same type (Number).
Acording to the algorithm, if both operands are Number and one of them is NaN, false is returned (step 1.c.i in the algorithm).
Explaining considering the scenario true == "true".
Straightway, the above returns false, however, our expectation was true
JavaScript uses Abstract Equality Comparison Algorithm, so according to the algorithm
true == "true"
// If one of the operands is Boolean, convert the Boolean operand to 1 if it is true and +0 if it is false
ConvertToNumber(true) == "true"
1 == "true"
// When the algorithm finds the above statements, it thinks that it needs to do one more conversion -
// "When comparing a number to a string, try to convert the string to a numeric value"
1 == ConvertToNumber("true)
1 == NaN
// Which returns false

What is !0 converted to when compared using == in JavaScript?

I was tring a few things in the console.
!5 is actually false
0 is a falsy value, so
0 == !5 is true
Okay, but when i tried this
!0 is true
5 is a truthy, so
5 == !0 should be true
But its not, the console says false. Why is this happening?
The reason the last line is false is that the == isn't a simple boolean conversion. It usually tries to convert operands with non-matching types down to a number.
So the 5 doesn't need conversion since it's already a number but !0, which is true, does. The value true gets converted to 1, so it doesn't equal 5.
You can infer from this that 1 == !0 will be true, and indeed it is.
This is detailed in the ES5 spec in the Abstract Equality Comparison Algorithm, step 7, which says of the comparison x == y:
If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
So the right-hand boolean is coerced to a number with ToNumber. In this case, ToNumber says:
The result is 1 if the argument is true.

Why if([]) is validated while [] == false in javascript?

if([] == false) alert('empty array is false');
alert(+[]) // alert 0
if([]) alert('empty array is true');
They both will run the alert
Demo
Both current answers here are correct, but I'd like to add a more detalied explanation based on the language specification. The reason for the apparently contradictory outcomes is that if statements and equality comparisons are evaluated differently.
In the case of an if(expression) statement, the expression is evaluated and then converted to the boolean type (§ 12.5). Arrays are Objects, and when an Object is converted to Boolean, the result is always true (§ 9.2).
Equality comparisons with == follow a different set of rules, detailed on § 11.9.3. The comparison may require multiple type conversions, until both operands are the same type. The order of the operands is also important. According to that algorithm, we can see that the comparison [] == false is actually a four-step operation:
There is a Boolean involved, so it's converted to a Number first (step 7 of the algorithm). So it becomes:
[] == 0
Then the array is converted to its primitive value (see § 9.1 and § 8.12.8), and becomes an empty string (step 9). So:
"" == 0
When comparing a String to a Number, the String is converted to Number first (step 5, following the rules described on § 9.3.1):
0 == 0
Now that we have two Numbers, the comparison evaluates to true according to step 1.c.iii.
It's because of type coercion of the == (equality) operator.
An empty array is considered truthy (just like an empty object), thus the second alert is called.
However, if you use ([] == false), your array is coerced to its string representation* which is "" which then is considered as a falsy value, which makes the condition true thus triggering the first alert too.
If you want to avoid type coercion, you have to use the === (identity) operator which is the preferred and by the famous Douglas Crockford promoted way to compare in javascript.
You can read more on that matter in this exhaustive answer.
*(Object.prototype.toString is called on it)
EDIT:
fun with JS-comparison:
NaN == false // false
NaN == true // also false
NaN == NaN // false
if(NaN) // false
if(!NaN) // true
0 == '0' // true
'' == 0 // true
'' == '0' // false !
This shows you the real "power" of Comparison with == due to the strange rules mentioned in bfavarettos answer.
There is a difference between evaluating a value as a boolean, and comparing it to true or false.
Whe using the == operator, the values are converted so that the types correspond. The [] value converted to the empty string "", and converting that in turn to a boolean gives false, so [] == false becomes true.
Evaluating [] as a boolean value will return true, because it is not a 'falsy' value, i.e. 0, false, null, "", NaN or undefined.

Categories