DEV Community

Cover image for Why (! + [] + [] + ![]).length is 9
Tomas Forsman
Tomas Forsman

Posted on

66 23

Why (! + [] + [] + ![]).length is 9

Edit: At the bottom I've added what I've figured out since I wrote this.

Today I sat down and figured out why you get this result in JavaScript

(! + [] + [] + ![]).length
> 9
Enter fullscreen mode Exit fullscreen mode

So I sat down with the js console and tried to figure things out. There were a lot of this going on:

(!"")
>true

(![])
>false

(!+[])
>true

(!+[]+[])
>"true"

(+[]+"2")
>"02"

([]+"2")
>"2"
Enter fullscreen mode Exit fullscreen mode

What I have come up with, and I might be totally off on some of these so please correct anything that doesn't look right, is the following.

  • JavaScript does operations from right to left.
  • The + operand first tries to add the left to the right, if that isn't possible it tries to convert the operand on the right to a number and if it can't do that it converts it to a string. If there is something to the left of the + it adds them together, and if it can't it sticks around. If there is nothing to the left it goes away.
  • The ! operand converts the operand to the right to a boolean and then reverts it. Only the following values are the same as false as booleans:
false
0
""
null
NaN
undefined
Enter fullscreen mode Exit fullscreen mode

All other values is not false and thus true.

So now it goes something like this:

// We start with
(! + [] + [] + ![]).length  // Remember that we are going from right to left starting 
                            // with the operation before doing -length
![] === false               // ! converts the content of [] to true, since it's not part of 
                            // the false group above, and then reverts it.
+ === +                     // the + is lonely so far with no operand to the right but sticks 
                            // around since it has something on the left.
[]+[] === ""                // The contents of [] can't be added or converted to a number so 
                            // the right operand becomes a string. There's still something to 
                            // the left so the + stays.
!+[] === true               // This the same as !+0 since +[] is 0


// Now we got: 
("false" + "" + false).length
""+false === "false"        // + adds "" to false. It then sticks around.
"true" + "" === "true"      // The + stays after adding "true" to ""


// ---
("true"++"false") 
+"false" ==== "false"       // + has nothing but an operand to the left so it goes away.
true + "" === "true"        // + adds them together and stays

// ---
("true"+"false")
"true" + "false" === "truefalse" // + still stays

// ---
(+"truefalse")
+"truefalse" === "truefalse"     // + has nothing to do after this so it goes away

// ---
("truefalse")               // The operation is done so now we are left with what's outside.

"truefalse".length === 9

Enter fullscreen mode Exit fullscreen mode

Yes, I did go through every step, even those that seems pointless. I'm not at all sure this is how it works but it is what seems to happen to me.

Thoughts?

Edit:
After comments and looking at the documentation this is now how I figure things going.
((!(+[]))+[]+(![]))
Unary operators are going right to left thus !+[] becomes +[] -> !0 === true.

Billboard image

Synthetic monitoring. Built for developers.

Join Vercel, Render, and thousands of other teams that trust Checkly to streamline monitor creation and configuration with Monitoring as Code.

Start Monitoring

Top comments (19)

Collapse
 
tpenguinltg profile image
tPenguinLTG β€’

JavaScript does operations from right to left.

In some cases it does, but not in this case. The reason you see behaviour that may appear to be right-to-left evaluation is because of operator precedence, where the unary ! has a higher precedence than the binary +.

+ actually evaluates from left to right, so more verbosely parenthesized, it looks like this:

(((!(+[])) + []) + (![])).length

Evaluating this parenthesized form step-by-step, we get this:

/*  1. */ (((!(+[])) + []) + (![])).length  // parethesized form.
/*  2. */ (((!0) + []) + (![])).length      // `+[]` is evaluated to `0`.
/*  3. */ (((!false) + []) + (![])).length  // `0` is coerced to the boolean `false`.
/*  4. */ ((true + []) + (![])).length      // `!false` is evaluated to `true`.
/*  5. */ (("true" + "") + (![])).length    // `[]` is coerced as a primitive to the string `""`, which means `true` is also coerced to a string.
/*  6. */ ("true" + (![])).length           // `"true" + ""` is string concatenation, which evaluates to `"true"`.
/*  7. */ ("true" + (!true)).length         // `[]` is coerced to the boolean `true`.
/*  8. */ ("true" + false).length           // `!true` evaluates to `false`.
/*  9. */ ("true" + "false").length         // `false` is coerced to a string because `"true"` is a string.
/* 10. */ "truefalse".length                // `"true" + "false"` is evaluated as string concatenation to `"truefalse".
/* 11. */ 9                                 // the length of `"truefalse"` is `9`.
Collapse
 
tomasforsman profile image
Tomas Forsman β€’

The first + is unary, right?

Collapse
 
tpenguinltg profile image
tPenguinLTG β€’

Indeed it is.

Collapse
 
karataev profile image
Eugene Karataev β€’

If I'm not sure what's going on with the JS code, I use ASTexplorer to look at the code with the compiler's eyes. It converts a code string into the tree of instructions to be executed by the compiler step by step.
But sometimes text representation of a tree is not expressive enough, so I built a little tool which visualizes an AST tree.
For (! + [] + [] + ![]).length AST in graph form looks like this:
ast
In runtime calculations starts from the bottom left and flow to the top.

Collapse
 
tomasforsman profile image
Tomas Forsman β€’

love this!

Collapse
 
jacobmparis profile image
Jacob Paris β€’ β€’ Edited
(({})[[]]+[]).length

This is also equal to 9 but for a very different reason

Collapse
 
tyskie profile image
tyskie β€’
(-1/0+[]).length

This one too but for yet another reason

Collapse
 
moopet profile image
Ben Sinclair β€’
(({})[[]]+[])[1]+(({})[[]]+[])[5]+(({})[[]]+[])[1]+(({})[[]]+[])[3]

Standing on the shoulders of giants.

Thread Thread
 
jacobmparis profile image
Jacob Paris β€’

You can stand on your own shoulders this was perfect

Collapse
 
kassem profile image
Kassem β€’

Interesting breakdown. I might have missed something but I think "truefalls" should be "truefalse".

Collapse
 
tomasforsman profile image
Tomas Forsman β€’

Indeed it should =D That's dyslexia for ya.

Collapse
 
tadman profile image
Scott Tadman β€’

Ain't that the truef.

Collapse
 
sagarb3 profile image
Sagar Bhattacharya β€’ β€’ Edited

It is a great observation , typecasting in JS is weird. This is the reason I love this platform.

Collapse
 
alexluong profile image
Alex Luong β€’

AWESOME INTERVIEW QUESTION!!!

Collapse
 
aerosboss profile image
aerosboss β€’

jajaja many brains will explode

Collapse
 
nabbisen profile image
nabbisen β€’

Totally greatly interesting.
Thank you for your great post.
The behaviors seem a bit strange and are beautiful πŸ˜†

Collapse
 
vocab_king profile image
WhistlerIAM β€’

Hi.
This is very well documented in the Boolean coercion abstract operation by ECMAScript language.
So, there are two operations that you may check out.

  1. ToBoolean and ToString.
Collapse
 
acido_binario profile image
🍝 sadder sCrypt Kiddie π–€πŸ•·οΈ β€’

Is this ease of programming?

Collapse
 
copy_pasta_chef profile image
Essien β€’

Does this have anything to do with the esoteric version of JS? en.m.wikipedia.org/wiki/JSFuck

Image of AssemblyAI

Automatic Speech Recognition with AssemblyAI

Experience near-human accuracy, low-latency performance, and advanced Speech AI capabilities with AssemblyAI's Speech-to-Text API. Sign up today and get $50 in API credit. No credit card required.

Try the API

πŸ‘‹ Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay