JavaScript: How Line Breaks and Missing Semicolons Can Break Your Code

Rules and Exceptions of Automatic Semicolon Insertion in JavaScript

Rucha Deshpande
JavaScript in Plain English

--

Image Courtesy — Merriam-Webster

In many programming languages, using a semicolon for terminating a statement is a must, however, in JavaScript, it is optional. You can omit the semicolon if the statements are written in separate lines. In this case, JavaScript adds the semicolon implicitly. But one should be very careful in understanding when to explicitly add the semicolon in the source code so as to not break the code and end up with unexpected results.

In this article, we will see the various rules and exceptions of Automatic Semicolon Insertion with examples.

Automatic Semicolon Insertion

Most ECMAScript statements and declarations must be terminated with a semicolon. Such semicolons may always appear explicitly in the source text. For convenience, however, such semicolons may be omitted from the source text in certain situations. These situations are described by saying that semicolons are automatically inserted into the source code token stream in those situations. - ECMAScript Language Specification.

In simple words, the JavaScript compiler adds a missing semicolon in the source code at the end of the statement. However, there are exceptions to this rule. JavaScript doesn’t always add a semicolon whenever there are line breaks.

Let’s see this with some examples.

Example 1

let a = 4
[1,2,3].map((arr) => {return arr*3})

What do you think would be the result? Well, it actually throws an error. Uncaught TypeError: Cannot read property ‘map’ of undefined

In this case, the JavaScript actually interprets the code as

let a = 4[1,2,3].map((arr) => {return arr*3})

When the code is being parsed from left to right without a semicolon, and it encounters [ , it is interpreted as the continuation of the previous statement.

To get the right result,

let a = 4;//use a semicolon 
[1,2,3].map((arr) => {return arr*3}) //[3,6,9]

Surprisingly, the below works!

let a
[1,2,3].map((arr) => {return arr*3}) //[3,6,9]

That’s because variable declarations(with let, var, const) are complete statements in themselves, the JavaScript compiler always terminates them.

Example 2

let a = 4
(3+4).toString()

The above code throws an error too. Uncaught TypeError: 4 is not a function

The explanation remains the same as that of [ . JavaScript compiler interprets the code as

let a = 4(3+4).toString()

Terminating the first statement gives the right result.

let a = 4; //use a semicolon
(3+4).toString() //"7"

Any statements that begin with ( , like IIFEs, expressions(logical, arithmetic, etc.,) fall under this exception.

var s = 23
(function add(a,b){return a + b})(2,3) //Uncaught TypeError: 23 is not a function
var a,b
a = 4, b = 7
( a > b ) ? alert(a) : alert(b) //Uncaught TypeError: 7 is not a function

Terminating the line before the statement gives the correct result.

Example 3

var a = 1+1
+"3" * 5
alert(a) //17

In the above code, the second line is interpreted as a continuation of the first.

var a = 1+1+"3"*5

Adding the semicolon gives the expected result.

var a = 1+1;
+"3" * 5
alert(a) //2

The same exception as that of [, (, applies to + , , / , `

Although it is rare that a statement might begin with +, -, / . But for a statement to begin with [ , ( is not uncommon and it is best to handle the terminations properly.

It is even acceptable to begin the statement with a ; to make sure that the statement is interpreted as a new one and not a continuation of the previous statement.

var s = 23
;(function add(a,b){return a + b})(2,3) //5

Example 4

function a(){
var x = 5
return
{
x
}
}
a(); //undefined

Why isn’t the return value correctly returned?

That’s because, whenever JavaScript encounters thereturn statement, and there is a line break after it, JavaScript automatically inserts a semicolon.

This means that, whenever you are using the return statement, the identifier after the return keyword must be in the same line. For example,

return 
true

This is actually interpreted as,

return; true;

Thereby returning no value.

Hence, in the example before, the { should begin after the return keyword on the same line.

function a(){
var x = 5
return{
x
}
}
a(); //{x:5}

The ‘No Line Terminator’ rule applies to:

  • PostfixExpressions (++ and --)
  • continue
  • break
  • return
  • yield, yield*
  • module
  • throw

The resulting practical advice to ECMAScript programmers is:

A postfix ++ or -- operator should appear on the same line as its operand.

An Expression in a return or throw statement or an AssignmentExpression in a yield expression should start on the same line as the return, throw, or yield token.

A LabelIdentifier in a break or continue statement should be on the same line as the break or continue token.

- ECMAScript Language Specification

Conclusion

We have seen various examples of rules and exceptions of Automatic Semicolon Insertion. To summarise,

  • If your statement begins with [, (, +, -, / , ` , make sure that there is a semicolon on the statement before to avoid errors.
  • While using return, continue, break , etc., make sure that the identifier after the keyword is on the same line.

That’s it. Hope this article was useful!

References:

--

--