Why does ++[[]][+[]]+[+[]] return the string “10”?
++[[]][+[]]+[+[]]
Why? What is happening here?
Solution:
If we split it up, the mess is equal to:
++[[]][+[]]
+
[+[]]
In JavaScript, it is true that
+[] === 0
. +
converts something into a number, and in this case it will come down to +""
or 0
(see specification details below).
Therefore, we can simplify it (
++
has precendence over +
):++[[]][0]
+
[0]
Because
[[]][0]
means: get the first element from [[]]
, it is true that:[[]][0]
returns the inner array ([]
). Due to references it's wrong to say[[]][0] === []
, but let's call the inner arrayA
to avoid the wrong notation.++[[]][0] == A + 1
, since++
means 'increment by one'.++[[]][0] === +(A + 1)
; in other words, it will always be a number (+1
does not necessarily return a number, whereas++
always does - thanks to Tim Down for pointing this out).
Again, we can simplify the mess into something more legible. Let's substitute
[]
back for A
:+([] + 1)
+
[0]
+([] + 1) === +("" + 1)
, and+("" + 1) === +("1")
, and+("1") === 1
Let's simplify it even more:
1
+
[0]
Also, this is true in JavaScript:
[0] == "0"
, because it's joining an array with one element. Joining will concatenate the elements separated by ,
. With one element, you can deduce that this logic will result in the first element itself.
So, in the end we obtain (number + string = string):
1
+
"0"
=== "10" // Yay!
Specification details for
+[]
:
This is quite a maze, but to do
+[]
, first it is being converted to a string because that's what +
says:11.4.6 Unary + OperatorThe unary + operator converts its operand to Number type.The production UnaryExpression : + UnaryExpression is evaluated as follows:
Let expr be the result of evaluating UnaryExpression. Return ToNumber(GetValue(expr)).
ToNumber()
says:ObjectApply the following steps:
Let primValue be ToPrimitive(input argument, hint String). Return ToString(primValue).
ToPrimitive()
says:ObjectReturn a default value for the Object. The default value of an object is retrieved by calling the [[DefaultValue]] internal method of the object, passing the optional hint PreferredType. The behaviour of the [[DefaultValue]] internal method is defined by this specification for all native ECMAScript objects in 8.12.8.
[[DefaultValue]]
says:8.12.8 [[DefaultValue]] (hint)When the [[DefaultValue]] internal method of O is called with hint String, the following steps are taken:
Let toString be the result of calling the [[Get]] internal method of object O with argument "toString". If IsCallable(toString) is true then,a. Let str be the result of calling the [[Call]] internal method of toString, with O as the this value and an empty argument list.b. If str is a primitive value, return str.
The
.toString
of an array says:15.4.4.2 Array.prototype.toString ( )When the toString method is called, the following steps are taken:
Let array be the result of calling ToObject on the this value. Let func be the result of calling the [[Get]] internal method of array with argument "join". If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2). Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
So
+[]
comes down to +""
, because [].join() === ""
.
Again, the
+
is defined as:11.4.6 Unary + OperatorThe unary + operator converts its operand to Number type.The production UnaryExpression : + UnaryExpression is evaluated as follows:
Let expr be the result of evaluating UnaryExpression. Return ToNumber(GetValue(expr)).
ToNumber
is defined for ""
as:The MV of StringNumericLiteral ::: [empty] is 0.
So
+"" === 0
, and thus +[] === 0
.
COMMENTS