In JavaScript, automatic semicolon insertion allows one to omit a semicolon at the end of a line. While you always should write semicolons, knowing how JavaScript handles their omission is important knowledge, because it helps you understand code without semicolons and because it has effects even in code with semicolons.
Expression versus statement:
3 * Math.sqrt(x) i++ obj.prop [ "a", "b", "c" ] { first: "Jane", last: "Doe" } function() {} // function expression
for(var i=0; i<3; i++) { console.log(i); } function twice(x) { // function declaration return 2 * x; } var foo = twice(21); // assignmentNote that the right hand side of the assignment is an expression.
while(a > 0) { a--; } // no semicolon do { a--; } while(a > 0);Example: function declaration versus function expression.
function foo() { } // no semicolon var foo = function() { };Note: if you do add a semicolon after the above mentioned statements, you do not get a syntax error, because it is considered an empty statement (see below).
The empty statement. A semicolon on its own is an empty statement and does nothing. Empty statements can appear anywhere a statement is expected. They are useful in situations where a statement is demanded, but not needed. In such situations, blocks are usually also allowed, but an empty block is longer than a semicolon. Example: The following two statements are equivalent.
while(processNextItem() > 0); while(processNextItem() > 0) {}The function processNextItem is assumed to return the number of remaining items. The following program is also syntactically correct: three empty statements.
;;;
Expressions as statements. Any expression can become a statement. Then it has to be terminated by a semicolon. Example:
"hello world"; a + b; sum(5, 3); a++;All of the above are expression statements. The first two have no effect.
The norm: The parser treats every new token as part of the current statement, unless there is a semicolon that terminates it. The following examples show code where you might think a semicolon should be inserted, but isn’t. This illustrates the risks of omitting semicolons.
No ASI:
a = b + c (d + e).print()This does not trigger ASI, because the opening parenthesis could follow c in a function call. The above is thus interpreted as:
a = b + c(d + e).print();No ASI:
a = b /hi/g.exec(c).map(d);No semicolon is inserted, the second line is not interpreted as a regular expression literal. Instead, the above is equivalent to:
a = b / hi / g.exec(c).map(d);No ASI:
var foo = "bar" [ "red", "green" ].foreach(function(c) { console.log(c) })No semicolon is inserted. Instead, the beginning of the second line is interpreted as an index for the string "bar"; the comma is allowed due to the comma operator (which evaluates both its left-hand side and its right-hand side and returns its right-hand side).
No ASI: In many browsers, the code below assigns 0 to func, because a++ is interpreted as the argument of an invocation of the function in the previous line.
var a = 0; var func = function(x) { return x } (a++)
Exceptions to the norm: ASI is applied in the following cases.
Example:
if (a < 0) a = 0 console.log(a)This triggers ASI and becomes
if (a < 0) a = 0; console.log(a);
For PostfixExpression, the rationale is avoiding the modification of a value on the previous line. For continue, break, return and throw, the rationale is that if they are used without an argument, they should not refer to the next line if one forgets a semicolon.
- PostfixExpression
- LeftHandSideExpression [no LineTerminator here] ++
- LeftHandSideExpression [no LineTerminator here] --
- ContinueStatement
- continue [no LineTerminator here] Identifier? ;
- BreakStatement
- break [no LineTerminator here] Identifier? ;
- ReturnStatement
- return [no LineTerminator here] Expression? ;
- ThrowStatement
- throw [no LineTerminator here] Expression? ;
Example:
a ++ cTriggers ASI and becomes
a; ++ cExample:
return a + bTriggers ASI and becomes
return; a + b;Example by Crockford:
return { ok: false; };Triggers ASI and is interpreted as an empty return statement, followed by a block (with the label ok and the expression statement false), followed by an empty statement (after the closing brace). Thus, if you want to return an object literal, do it as follows.
return { ok: false; };
function add(a,b) { return a+b }ASI turns this code into
function add(a,b) { return a+b; }
if (a > b) else c = dNormally, ASI would be triggered, because else cannot follow the if head. However, adding a semicolon after the head would create an empty statement and is thus not done. Accordingly, the above code causes a syntax error. However, if one manually inserts a semicolon, the result is syntactically correct.
if (a > b); else c = bNote that this rule is not necessary in the following example, where there is no danger of ASI, because the opening brace can follow the if head.
if (a > b) { c = a }
var obj = { // don’t move the brace to a new line name: "John" }; var arr = [ // don’t move the bracket to a new line 5, 13, 29 ];Compare:
return { name: "John" };