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:
!
evaluates before ==
.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?
If two distinct objects have the same number of properties, with the same names and values, they are still not equal. Two arrays that have the same elements in the same order are not equal to each other.
Empty arrays are true but they're also equal to false.
If the length of the object is 0, then the array is considered to be empty and the function will return TRUE. Else the array is not empty and the function will return False.
The sum of values in an array. If the array parameter value is an empty array, returns zero.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With