This is valid and returns the string "10" in JavaScript (more examples here):
console.log(++[[]][+[]]+[+[]])
Why? What is happening here?
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 array A to avoid the wrong notation.
++ before its operand means “increment by one and return the incremented result”. So ++[[]][0] is equivalent to Number(A) + 1 (or +A + 1).
Again, we can simplify the mess into something more legible. Let's substitute [] back for A:
(+[] + 1)
+
[0]
Before +[] can coerce the array into the number 0, it needs to be coerced into a string first, which is "", again. Finally, 1 is added, which results in 1.
(+[] + 1) === (+"" + 1)
(+"" + 1) === (0 + 1)
(0 + 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.
In this case, + sees two operands: a number and an array. It’s now trying to coerce the two into the same type. First, the array is coerced into the string "0", next, the number is coerced into a string ("1"). 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 + Operator
The 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:
Object
Apply the following steps:
Let primValue be ToPrimitive(input argument, hint String).
Return ToString(primValue).
ToPrimitive() says:
Object
Return 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 + Operator
The 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.
++[ [] ][+[]] === 1
+[] === 0
++[ [] ][0] === 1
[ +[] ] is [ 0 ]
Then we have a string concatenation:
1 + String([ 0 ]) === 10
The following is adapted from a blog post answering this question that I posted while this question was still closed. Links are to (an HTML copy of) the ECMAScript 3 spec, still the baseline for JavaScript in today's commonly used web browsers.
First, a comment: this kind of expression is never going to show up in any (sane) production environment and is only of any use as an exercise in just how well the reader knows the dirty edges of JavaScript. The general principle that JavaScript operators implicitly convert between types is useful, as are some of the common conversions, but much of the detail in this case is not.
The expression ++[[]][+[]]+[+[]] may initially look rather imposing and obscure, but is actually relatively easy break down into separate expressions. Below I’ve simply added parentheses for clarity; I can assure you they change nothing, but if you want to verify that then feel free to read up about the grouping operator. So, the expression can be more clearly written as
( ++[[]][+[]] ) + ( [+[]] )
Breaking this down, we can simplify by observing that +[] evaluates to 0. To satisfy yourself why this is true, check out the unary + operator and follow the slightly tortuous trail which ends up with ToPrimitive converting the empty array into an empty string, which is then finally converted to 0 by ToNumber. We can now substitute 0 for each instance of +[]:
( ++[[]][0] ) + [0]
Simpler already. As for ++[[]][0], that’s a combination of the prefix increment operator (++), an array literal defining an array with single element that is itself an empty array ([[]]) and a property accessor ([0]) called on the array defined by the array literal.
So, we can simplify [[]][0] to just [] and we have ++[], right? In fact, this is not the case because evaluating ++[] throws an error, which may initially seem confusing. However, a little thought about the nature of ++ makes this clear: it’s used to increment a variable (e.g. ++i) or an object property (e.g. ++obj.count). Not only does it evaluate to a value, it also stores that value somewhere. In the case of ++[], it has nowhere to put the new value (whatever it may be) because there is no reference to an object property or variable to update. In spec terms, this is covered by the internal PutValue operation, which is called by the prefix increment operator.
So then, what does ++[[]][0] do? Well, by similar logic as +[], the inner array is converted to 0 and this value is incremented by 1 to give us a final value of 1. The value of property 0 in the outer array is updated to 1 and the whole expression evaluates to 1.
This leaves us with
1 + [0]
... which is a simple use of the addition operator. Both operands are first converted to primitives and if either primitive value is a string, string concatenation is performed, otherwise numeric addition is performed. [0] converts to "0", so string concatenation is used, producing "10".
As a final aside, something that may not be immediately apparent is that overriding either one of the toString() or valueOf() methods of Array.prototype will change the result of the expression, because both are checked and used if present when converting an object into a primitive value. For example, the following
Array.prototype.toString = function() {
return "foo";
};
++[[]][+[]]+[+[]]
... produces "NaNfoo". Why this happens is left as an exercise for the reader...
Let’s make it simple:
++[[]][+[]]+[+[]] = "10"
var a = [[]][+[]];
var b = [+[]];
// so a == [] and b == [0]
++a;
// then a == 1 and b is still that array [0]
// when you sum the var a and an array, it will sum b as a string just like that:
1 + "0" = "10"
This one evaluates to the same but a bit smaller
+!![]+''+(+[])
[] - is an array is converted that is converted to 0 when you add or subtract from it, so hence +[] = 0
![] - evaluates to false, so hence !![] evaluates to true
+!![] - converts the true to a numeric value that evaluates to true, so in this case 1
+'' - appends an empty string to the expression causing the number to be converted to string
+[] - evaluates to 0
so is evaluates to
+(true) + '' + (0)
1 + '' + 0
"10"
So now you got that, try this one:
_=$=+[],++_+''+$
+[] evaluates to 0
[...] then summing (+ operation) it with anything converts array content to its string representation consisting of elements joined with comma.
Anything other like taking index of array (have grater priority than + operation) is ordinal and is nothing interesting.
Perhaps the shortest possible ways to evaluate an expression as "10" without digits are:
+!+[] + [+[]] // "10"
-~[] + [+[]] // "10"
Explanation
+!+[]:
+[] is evaluated as 0.
!0 is evaluated as true.
+true is evaluated as 1.
-~[] is the same as -(-1) which is evaluated as 1.
[+[]]:
+[] is evaluated as 0
[0] is an array with the single element 0.
Then, JS evaluates the 1 + [0], a Number + Array expression. Then the ECMA specification works: + operator converts both operands to a string by calling the ToPrimitive and ToString abstract operations. It operates as an additive function if both operands of an expression are numbers only. The trick is that arrays easily coerce their elements into a concatenated string representation.
Some examples:
1 + {} // "1[object Object]"
1 + [] // "1"
1 + new Date() // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"
[] + [] // ""
[1] + [2] // "12"
{} + {} // "[object Object][object Object]" ¹
{a:1} + {b:2} // "[object Object][object Object]" ¹
[1, {}] + [2, {}] // "1,[object Object]2,[object Object]"
¹: Note that each line is evaluated in an expression context. The first {…} is an object literal, not a block, as would be the case in a statement context. In a REPL, you may see {} + {} resulting in NaN, because most REPLs operate in a statement context; here, the first {} is a block, and the code is equivalent to {}; +{};, with the final expression statement (whose value becomes the result of the completion record) is NaN because the unary + coerces the object to a number.
++[[]][+[]]+[+[]]
^^^
|
v
++[[]][+[]]+[0]
^^^
|
v
++[[]][0]+[0]
^^^^^^^
|
v
++[]+[0]
^^^
|
v
++[]+"0"
^^^^
|
v
++0+"0"
^^^
|
v
1+"0"
^^^^^
|
v
"10"
The + operator coerces any non-number operand via .valueOf(). If that doesn't return a number then .toString() is invoked.
We can verify this simply with:
const x = [], y = [];
x.valueOf = () => (console.log('x.valueOf() has been called'), y.valueOf());
x.toString = () => (console.log('x.toString() has been called'), y.toString());
console.log(`+x -> ${+x}`);
So +[] is the same as coercing "" into a number which is 0.
If any operand is a string then + concatenates.
Step by steps of that, + turn value to a number and if you add to an empty array +[]...as it's empty and is equal to 0, it will
So from there, now look into your code, it's ++[[]][+[]]+[+[]]...
And there is plus between them ++[[]][+[]] + [+[]]
So these [+[]] will return [0] as they have an empty array which gets converted to 0 inside the other array...
So as imagine, the first value is a 2-dimensional array with one array inside... so [[]][+[]] will be equal to [[]][0] which will return []...
And at the end ++ convert it and increase it to 1...
So you can imagine, 1 + "0" will be "10"...
Unary plus given string converts to number
Increment operator given string converts and increments by 1
[] == ''. Empty String
+'' or +[] evaluates 0.
++[[]][+[]]+[+[]] = 10
++[''][0] + [0] : First part is gives zeroth element of the array which is empty string
1+0
10
Related
The 'Wat' talk for CodeMash 2012 basically points out a few bizarre quirks with Ruby and JavaScript.
I have made a JSFiddle of the results at http://jsfiddle.net/fe479/9/.
The behaviours specific to JavaScript (as I don't know Ruby) are listed below.
I found in the JSFiddle that some of my results didn't correspond with those in the video, and I am not sure why. I am, however, curious to know how JavaScript is handling working behind the scenes in each case.
Empty Array + Empty Array
[] + []
result:
<Empty String>
I am quite curious about the + operator when used with arrays in JavaScript.
This matches the video's result.
Empty Array + Object
[] + {}
result:
[Object]
This matches the video's result. What's going on here? Why is this an object. What does the + operator do?
Object + Empty Array
{} + []
result:
[Object]
This doesn't match the video. The video suggests that the result is 0, whereas I get [Object].
Object + Object
{} + {}
result:
[Object][Object]
This doesn't match the video either, and how does outputting a variable result in two objects? Maybe my JSFiddle is wrong.
Array(16).join("wat" - 1)
result:
NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
Doing wat + 1 results in wat1wat1wat1wat1...
I suspect this is just straightforward behaviour that trying to subtract a number from a string results in NaN.
Here's a list of explanations for the results you're seeing (and supposed to be seeing). The references I'm using are from the ECMA-262 standard.
[] + []
When using the addition operator, both the left and right operands are converted to primitives first (§11.6.1). As per §9.1, converting an object (in this case an array) to a primitive returns its default value, which for objects with a valid toString() method is the result of calling object.toString() (§8.12.8). For arrays this is the same as calling array.join() (§15.4.4.2). Joining an empty array results in an empty string, so step #7 of the addition operator returns the concatenation of two empty strings, which is the empty string.
[] + {}
Similar to [] + [], both operands are converted to primitives first. For "Object objects" (§15.2), this is again the result of calling object.toString(), which for non-null, non-undefined objects is "[object Object]" (§15.2.4.2).
{} + []
The {} here is not parsed as an object, but instead as an empty block (§12.1, at least as long as you're not forcing that statement to be an expression, but more about that later). The return value of empty blocks is empty, so the result of that statement is the same as +[]. The unary + operator (§11.4.6) returns ToNumber(ToPrimitive(operand)). As we already know, ToPrimitive([]) is the empty string, and according to §9.3.1, ToNumber("") is 0.
{} + {}
Similar to the previous case, the first {} is parsed as a block with empty return value. Again, +{} is the same as ToNumber(ToPrimitive({})), and ToPrimitive({}) is "[object Object]" (see [] + {}). So to get the result of +{}, we have to apply ToNumber on the string "[object Object]". When following the steps from §9.3.1, we get NaN as a result:
If the grammar cannot interpret the String as an expansion of StringNumericLiteral, then the result of ToNumber is NaN.
Array(16).join("wat" - 1)
As per §15.4.1.1 and §15.4.2.2, Array(16) creates a new array with length 16. To get the value of the argument to join, §11.6.2 steps #5 and #6 show that we have to convert both operands to a number using ToNumber. ToNumber(1) is simply 1 (§9.3), whereas ToNumber("wat") again is NaN as per §9.3.1. Following step 7 of §11.6.2, §11.6.3 dictates that
If either operand is NaN, the result is NaN.
So the argument to Array(16).join is NaN. Following §15.4.4.5 (Array.prototype.join), we have to call ToString on the argument, which is "NaN" (§9.8.1):
If m is NaN, return the String "NaN".
Following step 10 of §15.4.4.5, we get 15 repetitions of the concatenation of "NaN" and the empty string, which equals the result you're seeing.
When using "wat" + 1 instead of "wat" - 1 as argument, the addition operator converts 1 to a string instead of converting "wat" to a number, so it effectively calls Array(16).join("wat1").
As to why you're seeing different results for the {} + [] case: When using it as a function argument, you're forcing the statement to be an ExpressionStatement, which makes it impossible to parse {} as empty block, so it's instead parsed as an empty object literal.
This is more of a comment than an answer, but for some reason I can't comment on your question. I wanted to correct your JSFiddle code. However, I posted this on Hacker News and someone suggested that I repost it here.
The problem in the JSFiddle code is that ({}) (opening braces inside of parentheses) is not the same as {} (opening braces as the start of a line of code). So when you type out({} + []) you are forcing the {} to be something which it is not when you type {} + []. This is part of the overall 'wat'-ness of Javascript.
The basic idea was simple JavaScript wanted to allow both of these forms:
if (u)
v;
if (x) {
y;
z;
}
To do so, two interpretations were made of the opening brace: 1. it is not required and 2. it can appear anywhere.
This was a wrong move. Real code doesn't have an opening brace appearing in the middle of nowhere, and real code also tends to be more fragile when it uses the first form rather than the second. (About once every other month at my last job, I'd get called to a coworker's desk when their modifications to my code weren't working, and the problem was that they'd added a line to the "if" without adding curly braces. I eventually just adopted the habit that the curly braces are always required, even when you're only writing one line.)
Fortunately in many cases eval() will replicate the full wat-ness of JavaScript. The JSFiddle code should read:
function out(code) {
function format(x) {
return typeof x === "string" ?
JSON.stringify(x) : x;
}
document.writeln('>>> ' + code);
document.writeln(format(eval(code)));
}
document.writeln("<pre>");
out('[] + []');
out('[] + {}');
out('{} + []');
out('{} + {}');
out('Array(16).join("wat" + 1)');
out('Array(16).join("wat - 1")');
out('Array(16).join("wat" - 1) + " Batman!"');
document.writeln("</pre>");
[Also that is the first time I have written document.writeln in many many many years, and I feel a little dirty writing anything involving both document.writeln() and eval().]
I second #Ventero’s solution. If you want to, you can go into more detail as to how + converts its operands.
First step (§9.1): convert both operands to primitives (primitive values are undefined, null, booleans, numbers, strings; all other values are objects, including arrays and functions). If an operand is already primitive, you are done. If not, it is an object obj and the following steps are performed:
Call obj.valueOf(). If it returns a primitive, you are done. Direct instances of Object and arrays return themselves, so you are not done yet.
Call obj.toString(). If it returns a primitive, you are done. {} and [] both return a string, so you are done.
Otherwise, throw a TypeError.
For dates, step 1 and 2 are swapped. You can observe the conversion behavior as follows:
var obj = {
valueOf: function () {
console.log("valueOf");
return {}; // not a primitive
},
toString: function () {
console.log("toString");
return {}; // not a primitive
}
}
Interaction (Number() first converts to primitive then to number):
> Number(obj)
valueOf
toString
TypeError: Cannot convert object to primitive value
Second step (§11.6.1): If one of the operands is a string, the other operand is also converted to string and the result is produced by concatenating two strings. Otherwise, both operands are converted to numbers and the result is produced by adding them.
More detailed explanation of the conversion process: “What is {} + {} in JavaScript?”
We may refer to the specification and that's great and most accurate, but most of the cases can also be explained in a more comprehensible way with the following statements:
+ and - operators work only with primitive values. More specifically +(addition) works with either strings or numbers, and +(unary) and -(subtraction and unary) works only with numbers.
All native functions or operators that expect primitive value as argument, will first convert that argument to desired primitive type. It is done with valueOf or toString, which are available on any object. That's the reason why such functions or operators don't throw errors when invoked on objects.
So we may say that:
[] + [] is same as String([]) + String([]) which is same as '' + ''. I mentioned above that +(addition) is also valid for numbers, but there is no valid number representation of an array in JavaScript, so addition of strings is used instead.
[] + {} is same as String([]) + String({}) which is same as '' + '[object Object]'
{} + []. This one deserves more explanation (see Ventero answer). In that case, curly braces are treated not as an object but as an empty block, so it turns out to be same as +[]. Unary + works only with numbers, so the implementation tries to get a number out of []. First it tries valueOf which in the case of arrays returns the same object, so then it tries the last resort: conversion of a toString result to a number. We may write it as +Number(String([])) which is same as +Number('') which is same as +0.
Array(16).join("wat" - 1) subtraction - works only with numbers, so it's the same as: Array(16).join(Number("wat") - 1), as "wat" can't be converted to a valid number. We receive NaN, and any arithmetic operation on NaN results with NaN, so we have: Array(16).join(NaN).
To buttress what has been shared earlier.
The underlying cause of this behaviour is partly due to the weakly-typed nature of JavaScript. For example, the expression 1 + “2” is ambiguous since there are two possible interpretations based on the operand types (int, string) and (int int):
User intends to concatenate two strings, result: “12”
User intends to add two numbers, result: 3
Thus with varying input types,the output possibilities increase.
The addition algorithm
Coerce operands to primitive values
The JavaScript primitives are string, number, null, undefined and boolean (Symbol is coming soon in ES6). Any other value is an object (e.g. arrays, functions and objects). The coercion process for converting objects into primitive values is described thus:
If a primitive value is returned when object.valueOf() is invoked, then return this value, otherwise continue
If a primitive value is returned when object.toString() is invoked, then return this value, otherwise continue
Throw a TypeError
Note: For date values, the order is to invoke toString before valueOf.
If any operand value is a string, then do a string concatenation
Otherwise, convert both operands to their numeric value and then add these values
Knowing the various coercion values of types in JavaScript does help to make the confusing outputs clearer. See the coercion table below
+-----------------+-------------------+---------------+
| Primitive Value | String value | Numeric value |
+-----------------+-------------------+---------------+
| null | “null” | 0 |
| undefined | “undefined” | NaN |
| true | “true” | 1 |
| false | “false” | 0 |
| 123 | “123” | 123 |
| [] | “” | 0 |
| {} | “[object Object]” | NaN |
+-----------------+-------------------+---------------+
It is also good to know that JavaScript's + operator is left-associative as this determines what the output will be cases involving more than one + operation.
Leveraging the
Thus 1 + "2" will give "12" because any addition involving a string will always default to string concatenation.
You can read more examples in this blog post (disclaimer I wrote it).
The 'Wat' talk for CodeMash 2012 basically points out a few bizarre quirks with Ruby and JavaScript.
I have made a JSFiddle of the results at http://jsfiddle.net/fe479/9/.
The behaviours specific to JavaScript (as I don't know Ruby) are listed below.
I found in the JSFiddle that some of my results didn't correspond with those in the video, and I am not sure why. I am, however, curious to know how JavaScript is handling working behind the scenes in each case.
Empty Array + Empty Array
[] + []
result:
<Empty String>
I am quite curious about the + operator when used with arrays in JavaScript.
This matches the video's result.
Empty Array + Object
[] + {}
result:
[Object]
This matches the video's result. What's going on here? Why is this an object. What does the + operator do?
Object + Empty Array
{} + []
result:
[Object]
This doesn't match the video. The video suggests that the result is 0, whereas I get [Object].
Object + Object
{} + {}
result:
[Object][Object]
This doesn't match the video either, and how does outputting a variable result in two objects? Maybe my JSFiddle is wrong.
Array(16).join("wat" - 1)
result:
NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
Doing wat + 1 results in wat1wat1wat1wat1...
I suspect this is just straightforward behaviour that trying to subtract a number from a string results in NaN.
Here's a list of explanations for the results you're seeing (and supposed to be seeing). The references I'm using are from the ECMA-262 standard.
[] + []
When using the addition operator, both the left and right operands are converted to primitives first (§11.6.1). As per §9.1, converting an object (in this case an array) to a primitive returns its default value, which for objects with a valid toString() method is the result of calling object.toString() (§8.12.8). For arrays this is the same as calling array.join() (§15.4.4.2). Joining an empty array results in an empty string, so step #7 of the addition operator returns the concatenation of two empty strings, which is the empty string.
[] + {}
Similar to [] + [], both operands are converted to primitives first. For "Object objects" (§15.2), this is again the result of calling object.toString(), which for non-null, non-undefined objects is "[object Object]" (§15.2.4.2).
{} + []
The {} here is not parsed as an object, but instead as an empty block (§12.1, at least as long as you're not forcing that statement to be an expression, but more about that later). The return value of empty blocks is empty, so the result of that statement is the same as +[]. The unary + operator (§11.4.6) returns ToNumber(ToPrimitive(operand)). As we already know, ToPrimitive([]) is the empty string, and according to §9.3.1, ToNumber("") is 0.
{} + {}
Similar to the previous case, the first {} is parsed as a block with empty return value. Again, +{} is the same as ToNumber(ToPrimitive({})), and ToPrimitive({}) is "[object Object]" (see [] + {}). So to get the result of +{}, we have to apply ToNumber on the string "[object Object]". When following the steps from §9.3.1, we get NaN as a result:
If the grammar cannot interpret the String as an expansion of StringNumericLiteral, then the result of ToNumber is NaN.
Array(16).join("wat" - 1)
As per §15.4.1.1 and §15.4.2.2, Array(16) creates a new array with length 16. To get the value of the argument to join, §11.6.2 steps #5 and #6 show that we have to convert both operands to a number using ToNumber. ToNumber(1) is simply 1 (§9.3), whereas ToNumber("wat") again is NaN as per §9.3.1. Following step 7 of §11.6.2, §11.6.3 dictates that
If either operand is NaN, the result is NaN.
So the argument to Array(16).join is NaN. Following §15.4.4.5 (Array.prototype.join), we have to call ToString on the argument, which is "NaN" (§9.8.1):
If m is NaN, return the String "NaN".
Following step 10 of §15.4.4.5, we get 15 repetitions of the concatenation of "NaN" and the empty string, which equals the result you're seeing.
When using "wat" + 1 instead of "wat" - 1 as argument, the addition operator converts 1 to a string instead of converting "wat" to a number, so it effectively calls Array(16).join("wat1").
As to why you're seeing different results for the {} + [] case: When using it as a function argument, you're forcing the statement to be an ExpressionStatement, which makes it impossible to parse {} as empty block, so it's instead parsed as an empty object literal.
This is more of a comment than an answer, but for some reason I can't comment on your question. I wanted to correct your JSFiddle code. However, I posted this on Hacker News and someone suggested that I repost it here.
The problem in the JSFiddle code is that ({}) (opening braces inside of parentheses) is not the same as {} (opening braces as the start of a line of code). So when you type out({} + []) you are forcing the {} to be something which it is not when you type {} + []. This is part of the overall 'wat'-ness of Javascript.
The basic idea was simple JavaScript wanted to allow both of these forms:
if (u)
v;
if (x) {
y;
z;
}
To do so, two interpretations were made of the opening brace: 1. it is not required and 2. it can appear anywhere.
This was a wrong move. Real code doesn't have an opening brace appearing in the middle of nowhere, and real code also tends to be more fragile when it uses the first form rather than the second. (About once every other month at my last job, I'd get called to a coworker's desk when their modifications to my code weren't working, and the problem was that they'd added a line to the "if" without adding curly braces. I eventually just adopted the habit that the curly braces are always required, even when you're only writing one line.)
Fortunately in many cases eval() will replicate the full wat-ness of JavaScript. The JSFiddle code should read:
function out(code) {
function format(x) {
return typeof x === "string" ?
JSON.stringify(x) : x;
}
document.writeln('>>> ' + code);
document.writeln(format(eval(code)));
}
document.writeln("<pre>");
out('[] + []');
out('[] + {}');
out('{} + []');
out('{} + {}');
out('Array(16).join("wat" + 1)');
out('Array(16).join("wat - 1")');
out('Array(16).join("wat" - 1) + " Batman!"');
document.writeln("</pre>");
[Also that is the first time I have written document.writeln in many many many years, and I feel a little dirty writing anything involving both document.writeln() and eval().]
I second #Ventero’s solution. If you want to, you can go into more detail as to how + converts its operands.
First step (§9.1): convert both operands to primitives (primitive values are undefined, null, booleans, numbers, strings; all other values are objects, including arrays and functions). If an operand is already primitive, you are done. If not, it is an object obj and the following steps are performed:
Call obj.valueOf(). If it returns a primitive, you are done. Direct instances of Object and arrays return themselves, so you are not done yet.
Call obj.toString(). If it returns a primitive, you are done. {} and [] both return a string, so you are done.
Otherwise, throw a TypeError.
For dates, step 1 and 2 are swapped. You can observe the conversion behavior as follows:
var obj = {
valueOf: function () {
console.log("valueOf");
return {}; // not a primitive
},
toString: function () {
console.log("toString");
return {}; // not a primitive
}
}
Interaction (Number() first converts to primitive then to number):
> Number(obj)
valueOf
toString
TypeError: Cannot convert object to primitive value
Second step (§11.6.1): If one of the operands is a string, the other operand is also converted to string and the result is produced by concatenating two strings. Otherwise, both operands are converted to numbers and the result is produced by adding them.
More detailed explanation of the conversion process: “What is {} + {} in JavaScript?”
We may refer to the specification and that's great and most accurate, but most of the cases can also be explained in a more comprehensible way with the following statements:
+ and - operators work only with primitive values. More specifically +(addition) works with either strings or numbers, and +(unary) and -(subtraction and unary) works only with numbers.
All native functions or operators that expect primitive value as argument, will first convert that argument to desired primitive type. It is done with valueOf or toString, which are available on any object. That's the reason why such functions or operators don't throw errors when invoked on objects.
So we may say that:
[] + [] is same as String([]) + String([]) which is same as '' + ''. I mentioned above that +(addition) is also valid for numbers, but there is no valid number representation of an array in JavaScript, so addition of strings is used instead.
[] + {} is same as String([]) + String({}) which is same as '' + '[object Object]'
{} + []. This one deserves more explanation (see Ventero answer). In that case, curly braces are treated not as an object but as an empty block, so it turns out to be same as +[]. Unary + works only with numbers, so the implementation tries to get a number out of []. First it tries valueOf which in the case of arrays returns the same object, so then it tries the last resort: conversion of a toString result to a number. We may write it as +Number(String([])) which is same as +Number('') which is same as +0.
Array(16).join("wat" - 1) subtraction - works only with numbers, so it's the same as: Array(16).join(Number("wat") - 1), as "wat" can't be converted to a valid number. We receive NaN, and any arithmetic operation on NaN results with NaN, so we have: Array(16).join(NaN).
To buttress what has been shared earlier.
The underlying cause of this behaviour is partly due to the weakly-typed nature of JavaScript. For example, the expression 1 + “2” is ambiguous since there are two possible interpretations based on the operand types (int, string) and (int int):
User intends to concatenate two strings, result: “12”
User intends to add two numbers, result: 3
Thus with varying input types,the output possibilities increase.
The addition algorithm
Coerce operands to primitive values
The JavaScript primitives are string, number, null, undefined and boolean (Symbol is coming soon in ES6). Any other value is an object (e.g. arrays, functions and objects). The coercion process for converting objects into primitive values is described thus:
If a primitive value is returned when object.valueOf() is invoked, then return this value, otherwise continue
If a primitive value is returned when object.toString() is invoked, then return this value, otherwise continue
Throw a TypeError
Note: For date values, the order is to invoke toString before valueOf.
If any operand value is a string, then do a string concatenation
Otherwise, convert both operands to their numeric value and then add these values
Knowing the various coercion values of types in JavaScript does help to make the confusing outputs clearer. See the coercion table below
+-----------------+-------------------+---------------+
| Primitive Value | String value | Numeric value |
+-----------------+-------------------+---------------+
| null | “null” | 0 |
| undefined | “undefined” | NaN |
| true | “true” | 1 |
| false | “false” | 0 |
| 123 | “123” | 123 |
| [] | “” | 0 |
| {} | “[object Object]” | NaN |
+-----------------+-------------------+---------------+
It is also good to know that JavaScript's + operator is left-associative as this determines what the output will be cases involving more than one + operation.
Leveraging the
Thus 1 + "2" will give "12" because any addition involving a string will always default to string concatenation.
You can read more examples in this blog post (disclaimer I wrote it).
The 'Wat' talk for CodeMash 2012 basically points out a few bizarre quirks with Ruby and JavaScript.
I have made a JSFiddle of the results at http://jsfiddle.net/fe479/9/.
The behaviours specific to JavaScript (as I don't know Ruby) are listed below.
I found in the JSFiddle that some of my results didn't correspond with those in the video, and I am not sure why. I am, however, curious to know how JavaScript is handling working behind the scenes in each case.
Empty Array + Empty Array
[] + []
result:
<Empty String>
I am quite curious about the + operator when used with arrays in JavaScript.
This matches the video's result.
Empty Array + Object
[] + {}
result:
[Object]
This matches the video's result. What's going on here? Why is this an object. What does the + operator do?
Object + Empty Array
{} + []
result:
[Object]
This doesn't match the video. The video suggests that the result is 0, whereas I get [Object].
Object + Object
{} + {}
result:
[Object][Object]
This doesn't match the video either, and how does outputting a variable result in two objects? Maybe my JSFiddle is wrong.
Array(16).join("wat" - 1)
result:
NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
Doing wat + 1 results in wat1wat1wat1wat1...
I suspect this is just straightforward behaviour that trying to subtract a number from a string results in NaN.
Here's a list of explanations for the results you're seeing (and supposed to be seeing). The references I'm using are from the ECMA-262 standard.
[] + []
When using the addition operator, both the left and right operands are converted to primitives first (§11.6.1). As per §9.1, converting an object (in this case an array) to a primitive returns its default value, which for objects with a valid toString() method is the result of calling object.toString() (§8.12.8). For arrays this is the same as calling array.join() (§15.4.4.2). Joining an empty array results in an empty string, so step #7 of the addition operator returns the concatenation of two empty strings, which is the empty string.
[] + {}
Similar to [] + [], both operands are converted to primitives first. For "Object objects" (§15.2), this is again the result of calling object.toString(), which for non-null, non-undefined objects is "[object Object]" (§15.2.4.2).
{} + []
The {} here is not parsed as an object, but instead as an empty block (§12.1, at least as long as you're not forcing that statement to be an expression, but more about that later). The return value of empty blocks is empty, so the result of that statement is the same as +[]. The unary + operator (§11.4.6) returns ToNumber(ToPrimitive(operand)). As we already know, ToPrimitive([]) is the empty string, and according to §9.3.1, ToNumber("") is 0.
{} + {}
Similar to the previous case, the first {} is parsed as a block with empty return value. Again, +{} is the same as ToNumber(ToPrimitive({})), and ToPrimitive({}) is "[object Object]" (see [] + {}). So to get the result of +{}, we have to apply ToNumber on the string "[object Object]". When following the steps from §9.3.1, we get NaN as a result:
If the grammar cannot interpret the String as an expansion of StringNumericLiteral, then the result of ToNumber is NaN.
Array(16).join("wat" - 1)
As per §15.4.1.1 and §15.4.2.2, Array(16) creates a new array with length 16. To get the value of the argument to join, §11.6.2 steps #5 and #6 show that we have to convert both operands to a number using ToNumber. ToNumber(1) is simply 1 (§9.3), whereas ToNumber("wat") again is NaN as per §9.3.1. Following step 7 of §11.6.2, §11.6.3 dictates that
If either operand is NaN, the result is NaN.
So the argument to Array(16).join is NaN. Following §15.4.4.5 (Array.prototype.join), we have to call ToString on the argument, which is "NaN" (§9.8.1):
If m is NaN, return the String "NaN".
Following step 10 of §15.4.4.5, we get 15 repetitions of the concatenation of "NaN" and the empty string, which equals the result you're seeing.
When using "wat" + 1 instead of "wat" - 1 as argument, the addition operator converts 1 to a string instead of converting "wat" to a number, so it effectively calls Array(16).join("wat1").
As to why you're seeing different results for the {} + [] case: When using it as a function argument, you're forcing the statement to be an ExpressionStatement, which makes it impossible to parse {} as empty block, so it's instead parsed as an empty object literal.
This is more of a comment than an answer, but for some reason I can't comment on your question. I wanted to correct your JSFiddle code. However, I posted this on Hacker News and someone suggested that I repost it here.
The problem in the JSFiddle code is that ({}) (opening braces inside of parentheses) is not the same as {} (opening braces as the start of a line of code). So when you type out({} + []) you are forcing the {} to be something which it is not when you type {} + []. This is part of the overall 'wat'-ness of Javascript.
The basic idea was simple JavaScript wanted to allow both of these forms:
if (u)
v;
if (x) {
y;
z;
}
To do so, two interpretations were made of the opening brace: 1. it is not required and 2. it can appear anywhere.
This was a wrong move. Real code doesn't have an opening brace appearing in the middle of nowhere, and real code also tends to be more fragile when it uses the first form rather than the second. (About once every other month at my last job, I'd get called to a coworker's desk when their modifications to my code weren't working, and the problem was that they'd added a line to the "if" without adding curly braces. I eventually just adopted the habit that the curly braces are always required, even when you're only writing one line.)
Fortunately in many cases eval() will replicate the full wat-ness of JavaScript. The JSFiddle code should read:
function out(code) {
function format(x) {
return typeof x === "string" ?
JSON.stringify(x) : x;
}
document.writeln('>>> ' + code);
document.writeln(format(eval(code)));
}
document.writeln("<pre>");
out('[] + []');
out('[] + {}');
out('{} + []');
out('{} + {}');
out('Array(16).join("wat" + 1)');
out('Array(16).join("wat - 1")');
out('Array(16).join("wat" - 1) + " Batman!"');
document.writeln("</pre>");
[Also that is the first time I have written document.writeln in many many many years, and I feel a little dirty writing anything involving both document.writeln() and eval().]
I second #Ventero’s solution. If you want to, you can go into more detail as to how + converts its operands.
First step (§9.1): convert both operands to primitives (primitive values are undefined, null, booleans, numbers, strings; all other values are objects, including arrays and functions). If an operand is already primitive, you are done. If not, it is an object obj and the following steps are performed:
Call obj.valueOf(). If it returns a primitive, you are done. Direct instances of Object and arrays return themselves, so you are not done yet.
Call obj.toString(). If it returns a primitive, you are done. {} and [] both return a string, so you are done.
Otherwise, throw a TypeError.
For dates, step 1 and 2 are swapped. You can observe the conversion behavior as follows:
var obj = {
valueOf: function () {
console.log("valueOf");
return {}; // not a primitive
},
toString: function () {
console.log("toString");
return {}; // not a primitive
}
}
Interaction (Number() first converts to primitive then to number):
> Number(obj)
valueOf
toString
TypeError: Cannot convert object to primitive value
Second step (§11.6.1): If one of the operands is a string, the other operand is also converted to string and the result is produced by concatenating two strings. Otherwise, both operands are converted to numbers and the result is produced by adding them.
More detailed explanation of the conversion process: “What is {} + {} in JavaScript?”
We may refer to the specification and that's great and most accurate, but most of the cases can also be explained in a more comprehensible way with the following statements:
+ and - operators work only with primitive values. More specifically +(addition) works with either strings or numbers, and +(unary) and -(subtraction and unary) works only with numbers.
All native functions or operators that expect primitive value as argument, will first convert that argument to desired primitive type. It is done with valueOf or toString, which are available on any object. That's the reason why such functions or operators don't throw errors when invoked on objects.
So we may say that:
[] + [] is same as String([]) + String([]) which is same as '' + ''. I mentioned above that +(addition) is also valid for numbers, but there is no valid number representation of an array in JavaScript, so addition of strings is used instead.
[] + {} is same as String([]) + String({}) which is same as '' + '[object Object]'
{} + []. This one deserves more explanation (see Ventero answer). In that case, curly braces are treated not as an object but as an empty block, so it turns out to be same as +[]. Unary + works only with numbers, so the implementation tries to get a number out of []. First it tries valueOf which in the case of arrays returns the same object, so then it tries the last resort: conversion of a toString result to a number. We may write it as +Number(String([])) which is same as +Number('') which is same as +0.
Array(16).join("wat" - 1) subtraction - works only with numbers, so it's the same as: Array(16).join(Number("wat") - 1), as "wat" can't be converted to a valid number. We receive NaN, and any arithmetic operation on NaN results with NaN, so we have: Array(16).join(NaN).
To buttress what has been shared earlier.
The underlying cause of this behaviour is partly due to the weakly-typed nature of JavaScript. For example, the expression 1 + “2” is ambiguous since there are two possible interpretations based on the operand types (int, string) and (int int):
User intends to concatenate two strings, result: “12”
User intends to add two numbers, result: 3
Thus with varying input types,the output possibilities increase.
The addition algorithm
Coerce operands to primitive values
The JavaScript primitives are string, number, null, undefined and boolean (Symbol is coming soon in ES6). Any other value is an object (e.g. arrays, functions and objects). The coercion process for converting objects into primitive values is described thus:
If a primitive value is returned when object.valueOf() is invoked, then return this value, otherwise continue
If a primitive value is returned when object.toString() is invoked, then return this value, otherwise continue
Throw a TypeError
Note: For date values, the order is to invoke toString before valueOf.
If any operand value is a string, then do a string concatenation
Otherwise, convert both operands to their numeric value and then add these values
Knowing the various coercion values of types in JavaScript does help to make the confusing outputs clearer. See the coercion table below
+-----------------+-------------------+---------------+
| Primitive Value | String value | Numeric value |
+-----------------+-------------------+---------------+
| null | “null” | 0 |
| undefined | “undefined” | NaN |
| true | “true” | 1 |
| false | “false” | 0 |
| 123 | “123” | 123 |
| [] | “” | 0 |
| {} | “[object Object]” | NaN |
+-----------------+-------------------+---------------+
It is also good to know that JavaScript's + operator is left-associative as this determines what the output will be cases involving more than one + operation.
Leveraging the
Thus 1 + "2" will give "12" because any addition involving a string will always default to string concatenation.
You can read more examples in this blog post (disclaimer I wrote it).
This is valid and returns the string "10" in JavaScript (more examples here):
console.log(++[[]][+[]]+[+[]])
Why? What is happening here?
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 array A to avoid the wrong notation.
++ before its operand means “increment by one and return the incremented result”. So ++[[]][0] is equivalent to Number(A) + 1 (or +A + 1).
Again, we can simplify the mess into something more legible. Let's substitute [] back for A:
(+[] + 1)
+
[0]
Before +[] can coerce the array into the number 0, it needs to be coerced into a string first, which is "", again. Finally, 1 is added, which results in 1.
(+[] + 1) === (+"" + 1)
(+"" + 1) === (0 + 1)
(0 + 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.
In this case, + sees two operands: a number and an array. It’s now trying to coerce the two into the same type. First, the array is coerced into the string "0", next, the number is coerced into a string ("1"). 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 + Operator
The 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:
Object
Apply the following steps:
Let primValue be ToPrimitive(input argument, hint String).
Return ToString(primValue).
ToPrimitive() says:
Object
Return 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 + Operator
The 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.
++[ [] ][+[]] === 1
+[] === 0
++[ [] ][0] === 1
[ +[] ] is [ 0 ]
Then we have a string concatenation:
1 + String([ 0 ]) === 10
The following is adapted from a blog post answering this question that I posted while this question was still closed. Links are to (an HTML copy of) the ECMAScript 3 spec, still the baseline for JavaScript in today's commonly used web browsers.
First, a comment: this kind of expression is never going to show up in any (sane) production environment and is only of any use as an exercise in just how well the reader knows the dirty edges of JavaScript. The general principle that JavaScript operators implicitly convert between types is useful, as are some of the common conversions, but much of the detail in this case is not.
The expression ++[[]][+[]]+[+[]] may initially look rather imposing and obscure, but is actually relatively easy break down into separate expressions. Below I’ve simply added parentheses for clarity; I can assure you they change nothing, but if you want to verify that then feel free to read up about the grouping operator. So, the expression can be more clearly written as
( ++[[]][+[]] ) + ( [+[]] )
Breaking this down, we can simplify by observing that +[] evaluates to 0. To satisfy yourself why this is true, check out the unary + operator and follow the slightly tortuous trail which ends up with ToPrimitive converting the empty array into an empty string, which is then finally converted to 0 by ToNumber. We can now substitute 0 for each instance of +[]:
( ++[[]][0] ) + [0]
Simpler already. As for ++[[]][0], that’s a combination of the prefix increment operator (++), an array literal defining an array with single element that is itself an empty array ([[]]) and a property accessor ([0]) called on the array defined by the array literal.
So, we can simplify [[]][0] to just [] and we have ++[], right? In fact, this is not the case because evaluating ++[] throws an error, which may initially seem confusing. However, a little thought about the nature of ++ makes this clear: it’s used to increment a variable (e.g. ++i) or an object property (e.g. ++obj.count). Not only does it evaluate to a value, it also stores that value somewhere. In the case of ++[], it has nowhere to put the new value (whatever it may be) because there is no reference to an object property or variable to update. In spec terms, this is covered by the internal PutValue operation, which is called by the prefix increment operator.
So then, what does ++[[]][0] do? Well, by similar logic as +[], the inner array is converted to 0 and this value is incremented by 1 to give us a final value of 1. The value of property 0 in the outer array is updated to 1 and the whole expression evaluates to 1.
This leaves us with
1 + [0]
... which is a simple use of the addition operator. Both operands are first converted to primitives and if either primitive value is a string, string concatenation is performed, otherwise numeric addition is performed. [0] converts to "0", so string concatenation is used, producing "10".
As a final aside, something that may not be immediately apparent is that overriding either one of the toString() or valueOf() methods of Array.prototype will change the result of the expression, because both are checked and used if present when converting an object into a primitive value. For example, the following
Array.prototype.toString = function() {
return "foo";
};
++[[]][+[]]+[+[]]
... produces "NaNfoo". Why this happens is left as an exercise for the reader...
Let’s make it simple:
++[[]][+[]]+[+[]] = "10"
var a = [[]][+[]];
var b = [+[]];
// so a == [] and b == [0]
++a;
// then a == 1 and b is still that array [0]
// when you sum the var a and an array, it will sum b as a string just like that:
1 + "0" = "10"
This one evaluates to the same but a bit smaller
+!![]+''+(+[])
[] - is an array is converted that is converted to 0 when you add or subtract from it, so hence +[] = 0
![] - evaluates to false, so hence !![] evaluates to true
+!![] - converts the true to a numeric value that evaluates to true, so in this case 1
+'' - appends an empty string to the expression causing the number to be converted to string
+[] - evaluates to 0
so is evaluates to
+(true) + '' + (0)
1 + '' + 0
"10"
So now you got that, try this one:
_=$=+[],++_+''+$
+[] evaluates to 0
[...] then summing (+ operation) it with anything converts array content to its string representation consisting of elements joined with comma.
Anything other like taking index of array (have grater priority than + operation) is ordinal and is nothing interesting.
Perhaps the shortest possible ways to evaluate an expression as "10" without digits are:
+!+[] + [+[]] // "10"
-~[] + [+[]] // "10"
Explanation
+!+[]:
+[] is evaluated as 0.
!0 is evaluated as true.
+true is evaluated as 1.
-~[] is the same as -(-1) which is evaluated as 1.
[+[]]:
+[] is evaluated as 0
[0] is an array with the single element 0.
Then, JS evaluates the 1 + [0], a Number + Array expression. Then the ECMA specification works: + operator converts both operands to a string by calling the ToPrimitive and ToString abstract operations. It operates as an additive function if both operands of an expression are numbers only. The trick is that arrays easily coerce their elements into a concatenated string representation.
Some examples:
1 + {} // "1[object Object]"
1 + [] // "1"
1 + new Date() // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"
[] + [] // ""
[1] + [2] // "12"
{} + {} // "[object Object][object Object]" ¹
{a:1} + {b:2} // "[object Object][object Object]" ¹
[1, {}] + [2, {}] // "1,[object Object]2,[object Object]"
¹: Note that each line is evaluated in an expression context. The first {…} is an object literal, not a block, as would be the case in a statement context. In a REPL, you may see {} + {} resulting in NaN, because most REPLs operate in a statement context; here, the first {} is a block, and the code is equivalent to {}; +{};, with the final expression statement (whose value becomes the result of the completion record) is NaN because the unary + coerces the object to a number.
++[[]][+[]]+[+[]]
^^^
|
v
++[[]][+[]]+[0]
^^^
|
v
++[[]][0]+[0]
^^^^^^^
|
v
++[]+[0]
^^^
|
v
++[]+"0"
^^^^
|
v
++0+"0"
^^^
|
v
1+"0"
^^^^^
|
v
"10"
The + operator coerces any non-number operand via .valueOf(). If that doesn't return a number then .toString() is invoked.
We can verify this simply with:
const x = [], y = [];
x.valueOf = () => (console.log('x.valueOf() has been called'), y.valueOf());
x.toString = () => (console.log('x.toString() has been called'), y.toString());
console.log(`+x -> ${+x}`);
So +[] is the same as coercing "" into a number which is 0.
If any operand is a string then + concatenates.
Step by steps of that, + turn value to a number and if you add to an empty array +[]...as it's empty and is equal to 0, it will
So from there, now look into your code, it's ++[[]][+[]]+[+[]]...
And there is plus between them ++[[]][+[]] + [+[]]
So these [+[]] will return [0] as they have an empty array which gets converted to 0 inside the other array...
So as imagine, the first value is a 2-dimensional array with one array inside... so [[]][+[]] will be equal to [[]][0] which will return []...
And at the end ++ convert it and increase it to 1...
So you can imagine, 1 + "0" will be "10"...
Unary plus given string converts to number
Increment operator given string converts and increments by 1
[] == ''. Empty String
+'' or +[] evaluates 0.
++[[]][+[]]+[+[]] = 10
++[''][0] + [0] : First part is gives zeroth element of the array which is empty string
1+0
10
This is valid and returns the string "10" in JavaScript (more examples here):
console.log(++[[]][+[]]+[+[]])
Why? What is happening here?
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 array A to avoid the wrong notation.
++ before its operand means “increment by one and return the incremented result”. So ++[[]][0] is equivalent to Number(A) + 1 (or +A + 1).
Again, we can simplify the mess into something more legible. Let's substitute [] back for A:
(+[] + 1)
+
[0]
Before +[] can coerce the array into the number 0, it needs to be coerced into a string first, which is "", again. Finally, 1 is added, which results in 1.
(+[] + 1) === (+"" + 1)
(+"" + 1) === (0 + 1)
(0 + 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.
In this case, + sees two operands: a number and an array. It’s now trying to coerce the two into the same type. First, the array is coerced into the string "0", next, the number is coerced into a string ("1"). 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 + Operator
The 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:
Object
Apply the following steps:
Let primValue be ToPrimitive(input argument, hint String).
Return ToString(primValue).
ToPrimitive() says:
Object
Return 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 + Operator
The 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.
++[ [] ][+[]] === 1
+[] === 0
++[ [] ][0] === 1
[ +[] ] is [ 0 ]
Then we have a string concatenation:
1 + String([ 0 ]) === 10
The following is adapted from a blog post answering this question that I posted while this question was still closed. Links are to (an HTML copy of) the ECMAScript 3 spec, still the baseline for JavaScript in today's commonly used web browsers.
First, a comment: this kind of expression is never going to show up in any (sane) production environment and is only of any use as an exercise in just how well the reader knows the dirty edges of JavaScript. The general principle that JavaScript operators implicitly convert between types is useful, as are some of the common conversions, but much of the detail in this case is not.
The expression ++[[]][+[]]+[+[]] may initially look rather imposing and obscure, but is actually relatively easy break down into separate expressions. Below I’ve simply added parentheses for clarity; I can assure you they change nothing, but if you want to verify that then feel free to read up about the grouping operator. So, the expression can be more clearly written as
( ++[[]][+[]] ) + ( [+[]] )
Breaking this down, we can simplify by observing that +[] evaluates to 0. To satisfy yourself why this is true, check out the unary + operator and follow the slightly tortuous trail which ends up with ToPrimitive converting the empty array into an empty string, which is then finally converted to 0 by ToNumber. We can now substitute 0 for each instance of +[]:
( ++[[]][0] ) + [0]
Simpler already. As for ++[[]][0], that’s a combination of the prefix increment operator (++), an array literal defining an array with single element that is itself an empty array ([[]]) and a property accessor ([0]) called on the array defined by the array literal.
So, we can simplify [[]][0] to just [] and we have ++[], right? In fact, this is not the case because evaluating ++[] throws an error, which may initially seem confusing. However, a little thought about the nature of ++ makes this clear: it’s used to increment a variable (e.g. ++i) or an object property (e.g. ++obj.count). Not only does it evaluate to a value, it also stores that value somewhere. In the case of ++[], it has nowhere to put the new value (whatever it may be) because there is no reference to an object property or variable to update. In spec terms, this is covered by the internal PutValue operation, which is called by the prefix increment operator.
So then, what does ++[[]][0] do? Well, by similar logic as +[], the inner array is converted to 0 and this value is incremented by 1 to give us a final value of 1. The value of property 0 in the outer array is updated to 1 and the whole expression evaluates to 1.
This leaves us with
1 + [0]
... which is a simple use of the addition operator. Both operands are first converted to primitives and if either primitive value is a string, string concatenation is performed, otherwise numeric addition is performed. [0] converts to "0", so string concatenation is used, producing "10".
As a final aside, something that may not be immediately apparent is that overriding either one of the toString() or valueOf() methods of Array.prototype will change the result of the expression, because both are checked and used if present when converting an object into a primitive value. For example, the following
Array.prototype.toString = function() {
return "foo";
};
++[[]][+[]]+[+[]]
... produces "NaNfoo". Why this happens is left as an exercise for the reader...
Let’s make it simple:
++[[]][+[]]+[+[]] = "10"
var a = [[]][+[]];
var b = [+[]];
// so a == [] and b == [0]
++a;
// then a == 1 and b is still that array [0]
// when you sum the var a and an array, it will sum b as a string just like that:
1 + "0" = "10"
This one evaluates to the same but a bit smaller
+!![]+''+(+[])
[] - is an array is converted that is converted to 0 when you add or subtract from it, so hence +[] = 0
![] - evaluates to false, so hence !![] evaluates to true
+!![] - converts the true to a numeric value that evaluates to true, so in this case 1
+'' - appends an empty string to the expression causing the number to be converted to string
+[] - evaluates to 0
so is evaluates to
+(true) + '' + (0)
1 + '' + 0
"10"
So now you got that, try this one:
_=$=+[],++_+''+$
+[] evaluates to 0
[...] then summing (+ operation) it with anything converts array content to its string representation consisting of elements joined with comma.
Anything other like taking index of array (have grater priority than + operation) is ordinal and is nothing interesting.
Perhaps the shortest possible ways to evaluate an expression as "10" without digits are:
+!+[] + [+[]] // "10"
-~[] + [+[]] // "10"
Explanation
+!+[]:
+[] is evaluated as 0.
!0 is evaluated as true.
+true is evaluated as 1.
-~[] is the same as -(-1) which is evaluated as 1.
[+[]]:
+[] is evaluated as 0
[0] is an array with the single element 0.
Then, JS evaluates the 1 + [0], a Number + Array expression. Then the ECMA specification works: + operator converts both operands to a string by calling the ToPrimitive and ToString abstract operations. It operates as an additive function if both operands of an expression are numbers only. The trick is that arrays easily coerce their elements into a concatenated string representation.
Some examples:
1 + {} // "1[object Object]"
1 + [] // "1"
1 + new Date() // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"
[] + [] // ""
[1] + [2] // "12"
{} + {} // "[object Object][object Object]" ¹
{a:1} + {b:2} // "[object Object][object Object]" ¹
[1, {}] + [2, {}] // "1,[object Object]2,[object Object]"
¹: Note that each line is evaluated in an expression context. The first {…} is an object literal, not a block, as would be the case in a statement context. In a REPL, you may see {} + {} resulting in NaN, because most REPLs operate in a statement context; here, the first {} is a block, and the code is equivalent to {}; +{};, with the final expression statement (whose value becomes the result of the completion record) is NaN because the unary + coerces the object to a number.
++[[]][+[]]+[+[]]
^^^
|
v
++[[]][+[]]+[0]
^^^
|
v
++[[]][0]+[0]
^^^^^^^
|
v
++[]+[0]
^^^
|
v
++[]+"0"
^^^^
|
v
++0+"0"
^^^
|
v
1+"0"
^^^^^
|
v
"10"
The + operator coerces any non-number operand via .valueOf(). If that doesn't return a number then .toString() is invoked.
We can verify this simply with:
const x = [], y = [];
x.valueOf = () => (console.log('x.valueOf() has been called'), y.valueOf());
x.toString = () => (console.log('x.toString() has been called'), y.toString());
console.log(`+x -> ${+x}`);
So +[] is the same as coercing "" into a number which is 0.
If any operand is a string then + concatenates.
Step by steps of that, + turn value to a number and if you add to an empty array +[]...as it's empty and is equal to 0, it will
So from there, now look into your code, it's ++[[]][+[]]+[+[]]...
And there is plus between them ++[[]][+[]] + [+[]]
So these [+[]] will return [0] as they have an empty array which gets converted to 0 inside the other array...
So as imagine, the first value is a 2-dimensional array with one array inside... so [[]][+[]] will be equal to [[]][0] which will return []...
And at the end ++ convert it and increase it to 1...
So you can imagine, 1 + "0" will be "10"...
Unary plus given string converts to number
Increment operator given string converts and increments by 1
[] == ''. Empty String
+'' or +[] evaluates 0.
++[[]][+[]]+[+[]] = 10
++[''][0] + [0] : First part is gives zeroth element of the array which is empty string
1+0
10