JAVASCRIPT / GETTING STARTED
Statements, semicolons, and automatic insertion
Predict exactly where JavaScript ends a statement, so you can tell a harmless missing semicolon from one that silently changes your code's meaning.
What you will learn
- Separate two statements that share one line with an explicit semicolon
- Decide if a newline ends a statement by asking whether the next token can continue it
- Keep return, throw, break and continue on the same line as their value
- Protect a line that starts with ( or [ by putting a semicolon before it
Understanding Statements, semicolons, and automatic insertion
A statement is one instruction for the engine to carry out: a declaration, an assignment, a function call, a return. The grammar expects each one to be terminated by a semicolon, which is why two statements can share a single line as long as a semicolon sits between them. JavaScript is not indentation- or newline-delimited; a line break is just whitespace, exactly like a space or a tab.
Because unterminated statements are so common, the parser repairs them, and the repair rule is mechanical rather than clever. It reads tokens greedily, attaching each one to the statement it is currently building, and only when it hits a token that cannot possibly continue that statement, and that token sits on a new line, does it insert a semicolon in front of it. Two other situations trigger the same repair without needing a line break at all: a closing brace, and the end of the file. That is the whole mental model, and it explains the surprises: insertion happens because the parser got stuck, not because you pressed Enter.
The rule has a second half that pushes the other way. Immediately after return, throw, break, continue and yield, and between an operand and a postfix ++ or --, the grammar forbids a line break, so a newline in one of those spots forces a semicolon in even when the following line would have parsed perfectly well as a continuation. Those two halves produce the two classic bugs: a greedy parser silently swallows a following line that begins with ( or [, and a forced break turns return with its value on the next line into a plain return of undefined.
let a = 2; let b = 3; // one line, two statements: this semicolon is required
const sum = a
+ b; // no semicolon inserted after a, because + b can continue the expression
console.log(sum);
function total(x, y) {
return
x + y; // never runs: a semicolon was inserted right after return
}
console.log(total(2, 3));JavaScript ends a statement where the grammar can no longer extend it, not where you pressed Enter.
Worked examples
A line that begins with a parenthesis
Shows the parser continuing an expression across a newline and turning a function into a number.
function double(n) {
return n * 2;
}
const y = double
(4)
console.log(typeof y);
console.log(y);Example explained
Line 1The author wanted y to hold the function double, with (4) as a separate line.
Line 2A line break before ( does not end a statement, because ( can extend an expression into a call.
Line 3So the parser reads const y = double(4), and y is 8 instead of a function.
Line 4The semicolon is inserted before console instead, since an identifier cannot extend double(4).
A newline in front of ++
Shows a restricted production, where the line break forces a semicolon in rather than allowing a continuation.
let i = 0;
let j = 0;
i
++j
console.log("i =", i, "j =", j);Example explained
Line 1The grammar forbids a line break between an operand and a postfix ++, so i++ cannot span these two lines.
Line 2++ therefore becomes a token the parser cannot use, and a semicolon is inserted after i, leaving a statement that does nothing.
Line 3The next line is then read as the prefix increment ++j, so j becomes 1 and i stays 0.
Line 4Insertion prevented a syntax error here, but the result is the opposite of what the layout suggests.
One semicolon changes the value
Compares two identical layouts where only an explicit semicolon differs.
const parts = ["a", "b"];
const wrong = parts.join("+")
[0]
console.log(wrong);
const right = parts.join("+");
[0];
console.log(right);Example explained
Line 1parts.join("+") builds the string a+b, and [0] was meant to be a separate line.
Line 2In the first block [ continues the expression as a property access, so wrong is parts.join("+")[0], the character a.
Line 3In the second block the semicolon after join("+") ends the statement before [0] is read, so right keeps the full string.
Line 4When a line has to start with [ or (, writing the semicolon at the start of that line has the same effect.
Important notes
Insertion is deliberately skipped where it would be pointless: it will not invent an empty statement, and it will not supply the semicolons in a for header, so splitting for (let i = 0; i < 3; i++) across lines without them is a syntax error.
return followed by a newline is valid code, not a parse error, so the console stays quiet; only a linter rule about unreachable code will point at the abandoned line below it.
Common mistakes
Putting return on one line and { ok: true } on the next: the function returns undefined, and the leftover braces parse as a block rather than an object, so nothing is reported as an error.
Assuming a newline always ends a statement and then starting the next line with ( or [: the previous value gets called or indexed, giving either a wrong value or a runtime complaint that something is not a function.
Deleting every semicolon after hearing they are optional: the file keeps working until a line is reordered or a new line starts with ( , [ or a template literal, and the breakage then appears far from the edit.
Try it yourself
Change, predict, then run
In the console, write two functions that both try to return { ok: true }, one with the brace on the line after return and one with it on the same line, and log both results. Then write const items = [10, 20], put const n = items on the next line and [0] alone on the line after, and predict n before you run it.
Open the JavaScript workspaceCheck your understanding
A script contains const a = 1 on the first line, const b = a on the second, and (2) on the third, with no semicolons anywhere. What happens when it runs?
- b is 1, and (2) is evaluated as its own expression statement
- b is 2, because the parenthesised value replaces the assigned one
- It throws a TypeError, because the parser reads the second and third lines as a(2)
- It fails to parse, because a semicolon is missing before the third line
Show answer
The parser attaches tokens greedily, and ( can extend an expression into a call, so nothing is stuck and no semicolon is inserted: it reads const b = a(2) and then complains at run time that 1 is not a function. Option 0 is tempting because the line break looks like a boundary, but a newline only ends a statement when the next token cannot continue it, which is not the case for ( .