Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why and how does ([![]]+[][[]])[+!+[]+[+[]]] evaluate to the letter "i"? [duplicate]

While reading this article posted on dzone I found a snippet of JavaScript originally posted on Twitter by Marcus Lagergren.

The following code apparently prints the string "fail"

(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]];

This involves implicit type casting and I'm trying to understand how exactly this line is interpreted.

I've isolated each character

  • (![]+[])[+[]] prints "f"
  • (![]+[])[+!+[]] prints "a"
  • ([![]]+[][[]])[+!+[]+[+[]]] prints "i"
  • (![]+[])[!+[]+!+[]] prints "l"

I've also managed to break down the expressions returning each letter apart from "i"

letter "f"

![] an empty array is an Object, which according to ECMAScript documentation, point 9.2 evaluates to true when converted to a boolean so this is false

false+[] as per Point 11.6.1 both arguments of the binary + operator get converted to String, therefore we get "false"+"", which evaluates "false"

+[] a unary plus operator causes a ToNumber conversion followed by a ToPrimitive conversion if the argument is an Object. The result of such conversion is determined by calling the [[DefaultValue]] internal method of the object. In case of an empty array, it defaults to 0. (ECMAScript Documentation, sections: 11.4.6, 9.3, 9.1 )

"false"[0] we're accessing the character at index 0, hence the "f"

letter "a"

Same story, the only difference here are additional conversions in the part in square brackets (which evaluates to a number to point at another character in the string "false"), triggered by the use of unary + and ! operators.

+[] evaluates to 0, as explained above.

!0 evaluates to true as defined in Section 9.2 and Section 11.4.9. First, 0 is converted to a boolean false and then the operator inverts the value.

+true again, the unary plus triggers a ToNumber conversion, which returns a 1 for binary true (Section 11.4.6 and 9.3)

"false"[1] returns the second character in the string, which is "a"

letter "l"

!+[] evaluates to true as explained above

true+true using the binary + on primitives triggers a ToNumber conversion. In case of true, its result is 1 and 1+1 equals 2

"false"[2] - self explanatory

letter "i"

What leaves me stumped is the letter "i". I can see that the second part (in square brackets) evaluates to the string "10" and that the first part (in parentheses) returns "falseundefined" but I can't make heads or tails of how this is happening. Could someone explain it step by step? Especially the magic that happens with square brackets? (arrays and array access)

If possible, I'd like each step to contain a link to the underlying ECMAScript rules.

What I find the most cryptic is this part: [][[]]

like image 599
toniedzwiedz Avatar asked Jun 09 '13 22:06

toniedzwiedz


2 Answers

Your cryptic part isn't all that cryptic if you rewrite it a little:

[]['']

[] will be coerced into a string because it isn't an integer, so you're looking for a property of [] with the name '' (an empty string). You'll just get undefined, as there is no property with that name.

As for the actual letter, break the expression up into the two main components:

  • The string ([![]]+[][[]]):
    • [![]] is [false].
    • [][[]] is undefined.
    • Add them together and you get "falseundefined".
  • And the index: [+!+[]+[+[]]]. Some whitespace and parentheses will make the operations much clearer: [+(!(+[])) + [+[]]]:
    • [+[]] is [0].
    • +[] coerces [] to an integer, so you get 0.
    • !+[] coerces 0 to a boolean and negates it, so you get true.
    • +!+[] coerces true to an integer, so you get 1.
    • Add them together, and you get ["10"].

When using a string to access the properties of the array and the string happens to be an element of the array, the string is coerced into an integer and you get back the actual element of the array:

> [1, 2, 3]["0"]
1
> [1, 2, 3]["1"]
2

So your final result is:

> "falseundefined"["10"]
"i"

Read this answer for an explanation of the [false] + undefined part.

like image 125
Blender Avatar answered Sep 27 '22 19:09

Blender


([![]]+[][[]])[+!+[]+[+[]]] has two parts :

([![]]+[][[]]) and the other which you found yourself.

![] returns false. Then we use [...] to get the .toString() behavior of +. ([]+[] is the same as [].toString()+[].toString()) the [][[]] is undefined because we're trying to access the index [] (or [].toString(), which is '') of [] which is undefined.

Sorry for the previous answered, I misread your comment totally.

like image 28
Ven Avatar answered Sep 28 '22 19:09

Ven