7

I got hold of some obfuscated JavaScript code. I tried to understand it, and doing this, I typed pieces of it in the console. I can't understand why

> ((!!+[]+"")[+!![]])
< "a"

Why is ((!!+[]+"")[+!![]]) equal to "a" in JavaScript? Is there some other code snippets to get others letters?

I guess it's something to do with automatic casting.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
WayToDoor
  • 1,180
  • 9
  • 24

5 Answers5

8
( ( !!+[] + "" ) [ +!![] ] )
( (  !!0  + "" ) [ +true ] )
( ( false + "" ) [ +true ] )
( (   "false"  ) [   1   ] )
(         "false"[1]       )
(            "a"           ) 

Is there some other code snippets to get others letters ?

You can play with the same concept to get all the letters from "true", "false", "undefined", "NaN"...

pomber
  • 23,132
  • 10
  • 81
  • 94
4

You should work on operator precedence and type castings in JavaScript:

!!+[] // Is falsey. this is same for !!+0 or !!+""
false + "" // Is "false". as 5+"" is "5".

![] // Is falsey.
!false // Is true
+true //  Is equal to 1. +[] = 0, +false = 0

And at least,

"false"[1] // Is "a"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
marmeladze
  • 6,468
  • 3
  • 24
  • 45
  • 2
    Do you have a good reference for operator precedence in JavaScript? – Peter Mortensen May 28 '17 at 20:23
  • mozilla documentation is good enough i believe. i always refer there. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table – marmeladze May 28 '17 at 21:08
4

Let's use the console to get our answer assuming we don't know what any of this means, typing

[] + "" in the console outputs ""

Just putting (!!+[]) returns the Boolean false. If you append the Boolean false to "", you get the String false due to type coercion.

As expected, typing (!!+[]+"") outputs "false" to the console.

Moving on, in JavaScript, you can think of strings like an array of characters and you can access their character using the array notation.

So, in ((!!+[]+"")[+!![]]), you can remove the outermost brackets to make it seem simpler. Now we have (!!+[]+"")[+!![]] in which the first part in () returns the String "false", and the next part in [] accesses a character of the String "false". You can now bet that +!![] somehow returns 1 since "false"[1] equals "a".

Now let's find out how +!![] equals 1:

[] is an empty array which you can think of as 0 which would be true in JavaScript (because "in JavaScript anything 'real' is true"), so ![] is false and !![] is true.

Now we are left with +true which is just shorthand for convert true to a number which would be 1. Now you can see how +!![] evaluates to 1 and you understand (hopefully) how that obfuscated piece of code works!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ayush Seth
  • 1,169
  • 10
  • 21
3

The key to understand this is to know that JavaScript does implicit type conversions to evalute expressions it sees. In other words, while you might not know what it means to add a number to a string, JavaScript will make a guess rather than emitting an error. It is contrary to what you'd get in C++ which gives an explicit error in this case.

For example, +x always evaluates to a number, no matter what is type x actually is. Same thing for !x. Therefore, for your expression:

// A: !!+[]+"" which is evaluated like !(!(+[]))+""
+[]       === 0
!0        === true
!true     === false
false+''  === 'false'

// B: +!![] which is evaluated like +(!(![]))
![]       === false
!false    === true
+true     === 1

we get A[B] which is simply 'false'[1] === 'a'.

You can learn more about implicit type conversions and operator precedence at MDN.

Implicit type conversions is the reason why experienced JavaScript programmers prefer to use === rather than == when comparing values.

2

Here's detailed step by step process of what is happening:

( !! +[] + "" ) [ +!![] ]
//   ^^^ 

+[] Unary plus on array literal operates, which is equivalent Number([]) which results in 0. See this to why this evaluates to 0.

( !! 0 + "" ) [ +!![] ]
//^^^^

!!0 is equivalent to !!Boolean(0)) which evaluates to false since 0 is falsy value.

( false + "" ) [ +!![] ]
//^^^^^^^^^^^

false+"" is simple string concatenation therefore evaluates to "false"

"false" [ +!![] ]
//         ^^^^ 

!![] is equivalent to !!Boolean([]) and since Boolean conversion of objects returns always true. This evaluates to true.

"false" [ +true ]
//        ^^^^^

+true is equivalent to Number(true) which evaluates to 1.

"false" [ 1 ]

which is finally a.

The keypoint here is Javascript does implicit type conversion or Type Coercion while evaluating the expressions. To learn more about Type Coercion I suggest this excellent resource written by Dr. Axel Rauschmayer

Type Coercion

abhishekkannojia
  • 2,796
  • 23
  • 32