2 Mar 2018
6 min read
Anyway, I got it wrong the first couple of times I was trying to analyze it, and I had to run the code through a parser then consult the spec (and a JS guru!) to figure out what was going on. Since I learned some things in the process, I figured I'd share it with you.
The question posed to me was this: why does this first line "work" (compiles/runs) but the second line gives an error?
The thinking behind this riddle is that
[] should be the same as
, so either both should work or both should fail.
My first answer, after thinking about it for a few moments, was that these should both fail, but for different reasons. I was incorrect on several accounts. Indeed, the first one is valid (even if quite silly).
That's exactly why I challenge people to admit: "You Don't Know JS"; none of us ever fully knows something as complex as a programming language like JS. We learn some parts, and then learn more, and keep on learning. It's a forever process, not a destination.
First, I saw the two
++ operator usages, and my instinct was that these will both fail because the unary postfix
++, like in
x++, is mostly equivalent to
x = x + 1, which means the
x (whatever it is) has to be valid as something that can show up on the left-hand side of an
Actually, that last part is true, I was right about that, but for the wrong reasons.
What I wrongly thought was that
x++ is sorta like
x = x + 1; in that thinking,
 =  + 1 would be invalid. While that definitely looks weird, it's actually quite OK. In ES6, the
 = .. part is valid array destructuring.
x = x + 1 is misguided, lazy thinking, and I shouldn't be surprised that it led me astray.
Moreover, I was thinking of the first line all wrong, as well. What I thought was, the
[] is making an array (the outer
[ ]), and then the inner
 is trying to be a property access, which means it gets stringified (to
""), so it's like
[""]. This is nonsense. I dunno why my brain was messed up here.
Of course, for the outer
[ ] to be an array being accessed, it'd need to be like
x is the thing being accessed, not just
[] by itself. Anyway, thinking all wrong. Silly me.
Let's start with the easiest correction to thinking. Why is
To get the real answer, we should go to the official source of authority on such topics, the spec!
In spec-speak, the
x++ is a type of ["Update Expression"](http://www.ecma-international.org/ecma-262/8.0# sec-update-expressions) called the ["Postfix Increment Operator"](http://www.ecma-international.org/ecma-262/8.0# sec-postfix-increment-operator). It requires the
x part to be a valid ["Left-Hand Side Expression"](http://www.ecma-international.org/ecma-262/8.0# sec-left-hand-side-expressions) -- in a loose sense, an expression that is valid on the left-hand side of an
=. Actually, the more accurate way to think of it is not left-hand side of an
=, but rather, valid target of an assignment.
Looking at the list of valid expressions that can be targets of an assignment, we see things like ["Primary Expression"](http://www.ecma-international.org/ecma-262/8.0# prod-PrimaryExpression) and ["Member Expression"](http://www.ecma-international.org/ecma-262/8.0# prod-MemberExpression), among others.
If you look into Primary Expression, you find that an ["Array Literal"](http://www.ecma-international.org/ecma-262/8.0# prod-ArrayLiteral) (like our
!) is valid, at least from a syntax perspective.
 can be a left-hand side expression, and is thus valid to show up next to a
++. Hmmm. Why then does
++ give an error?
What you might miss, which I did, is: it's not a
SyntaxError at all! It's a runtime error called
Occassionally, I have people ask me about another perplexing -- and totally related! -- result in JS, that this code is valid syntax (but still fails at runtime):
2 = 3;
Obviously, a number literal shouldn't be something we can assign to. That makes no sense.
But it's not invalid syntax. It's just invalid runtime logic.
So what part of the spec makes
2 = 3 fail? The same reason that
2 = 3 fails is the same reason that
Both of these operations use an abstract algorithm in the spec called ["PutValue"](http://www.ecma-international.org/ecma-262/8.0# sec-putvalue). Step 3 of this algorithm says:
If Type(V) is not Reference, throw a ReferenceError exception.
Reference is a special specification type](http://www.ecma-international.org/ecma-262/8.0# sec-reference-specification-type) that refers to any kind of expression that represents an area in memory where some value could be assigned. In other words, to be a valid target, you have to be a
 are not
References, so that's why at runtime, you get a
ReferenceError; they are not valid assignment targets.
Don't worry, I haven't forgotten the first line of the snippet, which works. Remember, my thinking was all wrong about it, so I've got some correcting to do.
[] by itself is not an array access at all. It's just an array value that happens to contain another array value as its only contents. Think of it like this:
var a = ; var b = [a]; b; // []
[], what is that all about? Again, let's break it down with some temporary variables.
var a = ; var b = [a]; var c = b; c; //  -- aka, `a`!
So the original setup premise is correct.
[] is kinda the same as just
Back to that original question: why then does line 1 work but line 2 doesn't?
As we observed earlier, the ["Update Expression"](http://www.ecma-international.org/ecma-262/8.0# sec-update-expressions) requires a ["LeftHandSideExpression"](http://www.ecma-international.org/ecma-262/8.0# prod-LeftHandSideExpression). One of the valid types of those expressions is ["Member Expression"](http://www.ecma-international.org/ecma-262/8.0# prod-MemberExpression), like
x -- that's a member expression!
[] is a member expression.
So, we're good on syntax.
[]++ is valid.
But, wait! Wait! Wait!
 is not a
Reference, how could
[] -- which results in just
, remember! -- possibly be considered a
Reference so that
PutValue(..) (described above) doesn't throw an error?
This is where things get just a teeny bit tricky. Hat tip to my friend Allen-Wirfs Brock, former editor of the JS spec, for helping me connect the dots.
The result of a member expression is not the value itself (
), but rather a
Reference to that value -- see [Step 8 here](https://tc39.github.io/ecma262# sec-property-accessors-runtime-semantics-evaluation). So in fact, the
 access is giving us a reference to the 0th position of that outer array, rather than giving us the actual value in that position.
And that's why it's valid to use
[] as a left hand side expression: it actually is a
Reference after all!
As a matter of fact, the
++ does actually update the value, as we can see if we were to capture these values and inspect them later:
var a = []; a++; a; // 
a member expression gives us the
 array, and the
++ as a mathematical expression will coerce it to a primitive number, which is first
"" and then
++ then increments that to
1 and assigns it to
a. It's as if
a++ was actually
a = a + 1.
A little side note: if you run
[]++ in the console of your browser, it will report
++ returns the "original" value (well, after coercion, anyway -- see [step 2 here](https://tc39.github.io/ecma262# sec-postfix-increment-operator-runtime-semantics-evaluation)), not the updated value. So the
0 comes back, and the
1 gets put into the array via that
Of course, if you don't keep the outer array in a variable like we did, that update is moot since the value itself goes away. But it was updated, nonetheless.
I don't know if you appreciate JS or feel frustrated by all these nuances. But this endeavor makes me respect the language more, and renews my vigor to keep learning it even deeper. I think any programming language will have its nooks and crannies, some of which we like and some which drive us mad!
Ground Floor, Verse Building, 18 Brunswick Place, London, N1 6DZ
108 E 16th Street, New York, NY 10003
Join over 111,000 others and get access to exclusive content, job opportunities and more!