Automatic Semicolons

ASI (Automatic Semicolon Insertion) is when JavaScript assumes a ; in certain places in your JS program even if you didn’t put one there.

Why would it do that? Because if you omit even a single required ; your program would fail. Not very forgiving. ASI allows JS to be tolerant of certain places where ; aren’t commonly thought to be necessary.

It’s important to note that ASI will only take effect in the presence of a newline (aka line break). Semicolons are not inserted in the middle of a line.

Basically, if the JS parser parses a line where a parser error would occur (a missing expected ;), and it can reasonably insert one, it does so. What’s reasonable for insertion? Only if there’s nothing but whitespace and/or comments between the end of some statement and that line’s newline/line break.

Consider:

  1. var a = 42, b
  2. c;

Should JS treat the c on the next line as part of the var statement? It certainly would if a , had come anywhere (even another line) between b and c. But since there isn’t one, JS assumes instead that there’s an implied ; (at the newline) after b. Thus, c; is left as a standalone expression statement.

Similarly:

  1. var a = 42, b = "foo";
  2. a
  3. b // "foo"

That’s still a valid program without error, because expression statements also accept ASI.

There’s certain places where ASI is helpful, like for instance:

  1. var a = 42;
  2. do {
  3. // ..
  4. } while (a) // <-- ; expected here!
  5. a;

The grammar requires a ; after a do..while loop, but not after while or for loops. But most developers don’t remember that! So, ASI helpfully steps in and inserts one.

As we said earlier in the chapter, statement blocks do not require ; termination, so ASI isn’t necessary:

  1. var a = 42;
  2. while (a) {
  3. // ..
  4. } // <-- no ; expected here
  5. a;

The other major case where ASI kicks in is with the break, continue, return, and (ES6) yield keywords:

  1. function foo(a) {
  2. if (!a) return
  3. a *= 2;
  4. // ..
  5. }

The return statement doesn’t carry across the newline to the a *= 2 expression, as ASI assumes the ; terminating the return statement. Of course, return statements can easily break across multiple lines, just not when there’s nothing after return but the newline/line break.

  1. function foo(a) {
  2. return (
  3. a * 2 + 3 / 12
  4. );
  5. }

Identical reasoning applies to break, continue, and yield.

Error Correction

One of the most hotly contested religious wars in the JS community (besides tabs vs. spaces) is whether to rely heavily/exclusively on ASI or not.

Most, but not all, semicolons are optional, but the two ;s in the for ( .. ) .. loop header are required.

On the pro side of this debate, many developers believe that ASI is a useful mechanism that allows them to write more terse (and more “beautiful”) code by omitting all but the strictly required ;s (which are very few). It is often asserted that ASI makes many ;s optional, so a correctly written program without them is no different than a correctly written program with them.

On the con side of the debate, many other developers will assert that there are too many places that can be accidental gotchas, especially for newer, less experienced developers, where unintended ;s being magically inserted change the meaning. Similarly, some developers will argue that if they omit a semicolon, it’s a flat-out mistake, and they want their tools (linters, etc.) to catch it before the JS engine corrects the mistake under the covers.

Let me just share my perspective. A strict reading of the spec implies that ASI is an “error correction” routine. What kind of error, you may ask? Specifically, a parser error. In other words, in an attempt to have the parser fail less, ASI lets it be more tolerant.

But tolerant of what? In my view, the only way a parser error occurs is if it’s given an incorrect/errored program to parse. So, while ASI is strictly correcting parser errors, the only way it can get such errors is if there were first program authoring errors — omitting semicolons where the grammar rules require them.

So, to put it more bluntly, when I hear someone claim that they want to omit “optional semicolons,” my brain translates that claim to “I want to write the most parser-broken program I can that will still work.”

I find that to be a ludicrous position to take and the arguments of saving keystrokes and having more “beautiful code” to be weak at best.

Furthermore, I don’t agree that this is the same thing as the spaces vs tabs debate — that it’s purely cosmetic — but rather I believe it’s a fundamental question of writing code that adheres to grammar requirements vs. code that relies on grammar exceptions to just barely skate through.

Another way of looking at it is that relying on ASI is essentially considering newlines to be significant “whitespace.” Other languages like Python have true significant whitespace. But is it really appropriate to think of JavaScript as having significant newlines as it stands today?

My take: use semicolons wherever you know they are “required,” and limit your assumptions about ASI to a minimum.

But don’t just take my word for it. Back in 2012, creator of JavaScript Brendan Eich said (http://brendaneich.com/2012/04/the-infernal-semicolon/) the following:

The moral of this story: ASI is (formally speaking) a syntactic error correction procedure. If you start to code as if it were a universal significant-newline rule, you will get into trouble...I wish I had made newlines more significant in JS back in those ten days in May, 1995...Be careful not to use ASI as if it gave JS significant newlines.