Why does 2 == [2] in JavaScript?
I recently discovered that 2 == [2]
in JavaScript. As it turns out, this quirk has a couple of interesting consequences:
var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true
Similarly, the following works:
var a = { "abc" : 1 };
a[["abc"]] === a["abc"]; // this is also true
Even stranger still, this works as well:
[[[[[[[2]]]]]]] == 2; // this is true too! WTF?
These behaviors seem consistent across all browsers.
Any开发者_JAVA百科 idea why this is a language feature?
Here are more insane consequences of this "feature":
[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!
var a = [0];
a == a // true
a == !a // also true, WTF?
You can look up the comparison algorithm in the ECMA-spec (relevant sections of ECMA-262, 3rd edition for your problem: 11.9.3, 9.1, 8.6.2.6).
If you translate the involved abstract algorithms back to JS, what happens when evaluating 2 == [2]
is basically this:
2 === Number([2].valueOf().toString())
where valueOf()
for arrays returns the array itself and the string-representation of a one-element array is the string representation of the single element.
This also explains the third example as [[[[[[[2]]]]]]].toString()
is still just the string 2
.
As you can see, there's quite a lot of behind-the-scene magic involved, which is why I generally only use the strict equality operator ===
.
The first and second example are easier to follow as property names are always strings, so
a[[2]]
is equivalent to
a[[2].toString()]
which is just
a["2"]
Keep in mind that even numeric keys are treated as property names (ie strings) before any array-magic happens.
It is because of the implicit type conversion of ==
operator.
[2] is converted to Number is 2 when compared with a Number. Try the unary +
operator on [2].
> +[2]
2
var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true
On the right side of the equation, we have the a[2], which returns a number type with value 2. On the left, we are first creating a new array with a single object of 2. Then we are calling a[(array is in here)]. I am not sure if this evaluates to a string or a number. 2, or "2". Lets take the string case first. I believe a["2"] would create a new variable and return null. null !== 2. So lets assume it is actually implicitly converting to a number. a[2] would return 2. 2 and 2 match in type (so === works) and value. I think it is implicitly converting the array to a number because a[value] expects a string or number. It looks like number takes higher precedence.
On a side note, I wonder who determines that precedence. Is because [2] has a number as it's first item, so it converts to a number? Or is it that when passing an array into a[array] it tries to turn the array into a number first, then string. Who knows?
var a = { "abc" : 1 };
a[["abc"]] === a["abc"];
In this example, you are creating an object called a with a member called abc. The right side of the equation is pretty simple; it is equivalent to a.abc. This returns 1. The left side first creates a literal array of ["abc"]. You then search for a variable on the a object by passing in the newly created array. Since this expects a string, it converts the array into a string. This now evaluates to a["abc"], which equals 1. 1 and 1 are the same type (which is why === works) and equal value.
[[[[[[[2]]]]]]] == 2;
This is just an implicit conversion. === wouldn't work in this situation because there is a type mismatch.
For the ==
case, this is why Doug Crockford recommends always using ===
. It doesn't do any implicit type conversion.
For the examples with ===
, the implicit type conversion is done before the equality operator is called.
[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!
That's interesting, it's not that [0] is both true and false, actually
[0] == true // false
It is javascript's funny way of processing if() operator.
A array of one item can be treated as the item itself.
This is due to duck typing. Since "2" == 2 == [2] and possibly more.
To add a little detail to the other answers... when comparing an Array
to a Number
, Javascript will convert the Array
with parseFloat(array)
. You can try it yourself in the console (eg Firebug or Web Inspector) to see what different Array
values get converted to.
parseFloat([2]); // 2
parseFloat([2, 3]); // 2
parseFloat(['', 2]); // NaN
For Array
s, parseFloat
performs the operation on the Array
's first member, and discards the rest.
Edit: Per Christoph's details, it may be that it is using the longer form internally, but the results are consistently identical to parseFloat
, so you can always use parseFloat(array)
as shorthand to know for sure how it will be converted.
You are comparing 2 objects in every case.. Dont use ==, if you are thinking of comparison, you are having === in mind and not ==. == can often give insane effects. Look for the good parts in the language :)
Explanation for the EDIT section of the question:
1st Example
[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!
First typecast [0] to a primitive value as per Christoph's answer above we have "0" ([0].valueOf().toString()
)
"0" == false
Now, typecast Boolean(false) to Number and then String("0") to Number
Number("0") == Number(false)
or 0 == 0
so, [0] == false // true
As for if
statement, if there is not an explicit comparison in the if condition itself, the condition evaluates for truthy values.
There are only 6 falsy values: false, null, undefined, 0, NaN and empty string "". And anything that is not a falsy value is a truthy value.
Since [0] is not a falsy value, it is a truthy value, the if
statement evaluates to true & executes the statement.
2nd Example
var a = [0];
a == a // true
a == !a // also true, WTF?
Again type casting the values to primitive,
a = a
or [0].valueOf().toString() == [0].valueOf().toString()
or "0" == "0" // true; same type, same value
a == !a
or [0].valueOf().toString() == [0].valueOf().toString()
or "0" == !"0"
or "0" == false
or Number("0") == Number(false)
or 0 = 0 // true
精彩评论