Alexander Dickson

Fuckin' semicolons, how do they work?

No language’s semi-colons have invoked so much confusion as JavaScript’s. Like Ruby, the semi-colons are optional in the source. However, the JavaScript parser is meant to insert semi-colons where required (or know the rules to parse the source in such a way). This is known as Automatic Semi-colon Insertion, or ASI.

When does a semi-colon get inserted?

When in doubt, use the source, or at least the language specification. Summarised, it says…

  1. If a token is encountered that violates the grammar of the language, insert a semi-colon before it if there is a LineTerminator separating them or if the token is a }.
  2. If the entire program is parsed and the token stream appears to be invalid, insert a semi-colon at the end.
  3. If the grammar defines [no LineTerminator here], and a LineTerminator appears, insert a semi-colon.

There are a number of gotchas and general handy things to know when you’re letting the parser insert semi-colons for you.

Restricted Productions

The most famous example you’ll see is…

var fn = function() {
    return
        2 + 3;
}

…which will always return undefined. There is a pesky semi-colon gate crashing the party!

The return statement’s grammar is defined as…

ReturnStatement : return ;

return [no LineTerminator here] Expression ;

This means, when specifying a return value, you can not have a line terminator between the value and the return keyword, or the parser inserts a semi-colon and your function will always return undefined at that exit point.

This grammar also affects PostfixExpression (++n or --n), ContinueStatement (continue label), BreakStatement (break label) and ThrowStatement (throw new Error).

Looks like function invocation, sir

Check out this code…

var a = 2 + 3

(function () {
   a++;
})();

When this code is parsed, a semi-colon is not inserted after the 3, which attempts to invoke 3 as a function (with the anonymous function as the argument). A Number doesn’t implement [Call], so we get a TypeError in this example.

Regular expression literals

Regex literals in JavaScript have a number of problems, including an ASI gotcha. When the parser encounters a regex literal, it will never insert a semi-colon before one. The following code will cause a SyntaxError, in which the parser thinks we’re trying to divide 3 over ^.

var a = "2" + 3

/^\d+$/.test(a);

No empty statements

The parser will never insert a semi-colon if it’ll mean it will create an empty statement.

for loops

A semi-colon is never inserted as part of the header of a for loop.

Should I always use semi-colons?

That’s up to you. The majority of people will recommend that you do, and I’d tend to agree. If you decide not to, you better damn know how they work.


Want to discuss this post? Just mention me @alexdickson.