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""
intoboolean
so it takes that value and makes it intofalse
then negates intotrue
.==
prefers to pare numbers so in my thinkingtrue
makes1
and[]
is converted into""
and then0
Why does it work then? Where did I get it wrong?
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""
intoboolean
so it takes that value and makes it intofalse
then negates intotrue
.==
prefers to pare numbers so in my thinkingtrue
makes1
and[]
is converted into""
and then0
Why does it work then? Where did I get it wrong?
Share Improve this question edited Aug 1, 2022 at 10:00 Donald Duck 8,89223 gold badges79 silver badges102 bronze badges asked Dec 3, 2017 at 19:45 Konrad AlbrechtKonrad Albrecht 1,8992 gold badges12 silver badges22 bronze badges 3-
1
![]
isfalse
, nottrue
, because[]
is an object.ToPrimitive
is not called before negation, it’s called at the point of negation, if anything.==
coerces the first array. – Sebastian Simon Commented Dec 3, 2017 at 19:50 - yes ![] IS false but HOW it works? WHY its false? These are the real questions. The explanation that empty array is an object does not convince me sorry – Konrad Albrecht Commented Dec 3, 2017 at 20:09
-
2
@KonradAlbrecht, yet that is the explanation. If you are not satisfied with that, then that is really your problem. In JavaScript all objects (except
null
which is not really an object anddocument.all
, which is a whole different story) are truthy. There is nothing to understand about that, it is a given. Arrays are objects, and so[]
is truthy. – trincot Commented Dec 3, 2017 at 21:05
3 Answers
Reset to default 14Why 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 parison bees [] == 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 parison ToNumber(x) == y.
- If Type(y) is boolean, return the result of the parison x == ToNumber(y)
At this point, the parison bees [] == 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 parison ToPrimitive(x) == y.
As @Jonas_W stated, an array's ToPrimitive
will call its toString
, which will return a ma-separated list of its contents (I am oversimplifying).
At this point, the parison bees "" == 0
.
4. And finally (well, almost), this rule:
If Type(x) is String and Type(y) is Number, return the result of the parison ToNumber(x) == y.
An empty string converted to a number is 0
(Number("") == 0
is true
).
At this point, the parison bees 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 parison 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 paring 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.