Hacker News new | ask | show | jobs
by jeresig 5956 days ago
To start, you need to know that JavaScript has type coercion. Numbers can become strings, strings can become numbers, and arrays can become strings.

Thus, for example: [] in a string context becomes "". "" in a number context becomes 0. Thus if we were to do: "" - 1 we'd get the number -1. Additionally if we were to do [] - 1 we'd get -1. ++ is also able to coerce a string into a number.

If ++ is able to turn a string into a number then you're probably wondering why they just don't do ++[] and be done with it (to get 1, that is). The problem is that ++ is attempting to assign that resulting value back somewhere and since [] doesn't exist anywhere yet (it's temporary) it throws an error instead.

We could do something like this:

    var a = [];
    ++a; // 1
But we can't actually assign the result to a variable because we can't use alphanumeric characters. So what's another way in which we can assign the results to a variable? We can stick it into an array.

    var a = [ [] ];
    ++a[ 0 ]; // 1
This also works:

    ++[ [] ][ 0 ];
Now, we can't use 0 because it's alphanumeric so we swap 0 for +[] (+ turns the string "" into the number 0, [] turns into the string ""). Thus we get the final result:

    ++[ [] ][ +[] ]
6 comments

The explanation of why ++[] throws error is actually incorrect. This has nothing to do with "existence" of [] (I'm not even sure what existence could mean here). Rather, it's the way ++ operator works.

++ requires its operand to evaluate to a _reference_, not a _value_. If operand evaluates to a value, a ReferenceError is thrown (well, actually it is internal PutValue method that throws error, but that's irrelevant here). Now, if we were to pass reference to ++ operator, it would "work" as expected. E.g.:

++({ x: 1 }).x;

In this case, `({ x: 1 }).x` is a reference, it evaluates to 1, and is then increased by 1 to produce 2 as a resulting value.

When you said "this also works" with `++[ [] ][ 0 ]` example, what happened is that `[ [] ][ 0 ]` evaluated to a reference too, and returned a value of array (inner one). The fact that something like ++[[]][0] "works" should already hint at the fact that "existence" of an object is a bogus reasoning ;)

So what we really need to understand here (besides type coercion) is the notion of references. Literals, for example, always evaluate to values: 1, "foo", true, ({ x: 1 }), and [] are all values. Expressions involving property accessors, on the other hand — foo.bar, [1][0], ({ x: 1 }).x — are all references.

Understanding this distinction can also help explain other aspects of Javascript, such as, say, simple assignments, where left hand operand is constrained by the same rules as ++ operator; it should always evaluate to a reference:

1 = 1; // ReferenceError, LHS operand is not a reference

null = 1; // ReferenceError, LHS operand is not a reference

// but

({ x: 1 }).x = 1; // works as expected, LHS operand is a reference

x = 1; // works as expected, LHS operand is a reference

FWIW, it is the same internal PutValue method that's invoked by `=` operator, and is why assignment to non-reference values result in an error.

"This has nothing to do with 'existence' of [] (I'm not even sure what existence could mean here)."

Ah sorry, I was referring to the fact that the array was already created and within another object that we were referencing. But yeah, thanks for your more-in-depth explanation!

Qualification test to be able to join the jQuery team
You can even do that in half the bytes by making use of all unary operators together:

  -~+[]
-~ is a good proxy for ++ when incrementing unassigned variables.
That's some JavaScript kung-fu skill.

Excellent comment, thanks.

I did not get this line.

But we can't actually assign the result to a variable because we can't use alphanumeric characters.

You can't just write "var a = ..." because the goal is to use only "[]{}()+/."
damn, that is clever - I think it may have usurped the django pony in terms of magic ;)