Syntax, semicolons, comments and strict mode

Beginner30 min

Write readable JavaScript, understand where automatic semicolon insertion can bite, and know what strict mode changes.

What you will learn
  • Tell a statement from an expression
  • Use semicolons, comments and naming conventions consistently
  • Spot the cases where automatic semicolon insertion changes your meaning
  • Explain what strict mode changes and why modules are always strict
On this page

JavaScript forgives a lot of sloppy formatting, and that is exactly the problem: some forgiven mistakes change what your program does without any error. This lesson sets out the habits that avoid those traps, and then looks at strict mode, which turns several silent mistakes into errors.

Statements and expressions

An expression is a piece of code that produces a value: 5 + 3, "hi".length, age >= 18, a call such as Math.max(1, 2). A statement is a complete instruction that does something: declaring a variable, an if, a loop. A program is a list of statements, and many statements contain expressions.

expr_stmt.mjs
const result = 5 + 3;
console.log(result * 2);
Output
16

Here 5 + 3 is an expression with the value 8, and const result = ...; is a statement that stores it. result * 2 and console.log(...) are expressions too, and the line that contains console.log(...) is a statement.

The difference matters because you can put an expression wherever a value is expected, and you cannot do that with a statement. This is a SyntaxError, because if is a statement and has no value:

const label = if (age > 18) { "adult" }; // SyntaxError

JavaScript has an expression for the same job, the conditional operator, which the conditionals lesson covers.

Semicolons

A semicolon ends a statement. You may leave it out at the end of a line, because the language adds the missing ones itself with a rule called automatic semicolon insertion, or ASI. The rule is short to state and awkward to apply: if the next line cannot continue the current statement, a semicolon goes between them.

This is fine, because each line is a complete statement:

asi_lines.mjs
const a = 1
const b = 2
const total = a + b
console.log(total)
Output
3

This course writes the semicolons, and formatters such as Prettier add them by default. Many teams skip them. Both are legal styles, but whichever you pick, you must know where ASI goes wrong.

Lines that start with a bracket

A line that begins with (, [, a backtick, +, - or / continues the line before it, so no semicolon is inserted there. This runs without a syntax error and fails at run time:

const price = 20
const tax = 5
const total = price + tax
(price * 2)

JavaScript reads the last two lines as one: price + tax(price * 2). Node stops with this error, because tax is the number 5 and cannot be called:

TypeError: tax is not a function

With semicolons, each line stands alone and the intent is clear:

asi_guarded.mjs
const price = 20;
const tax = 5;
const total = price + tax;
console.log(total);
Output
25

If you choose to skip semicolons, a line that starts with one of those characters needs a semicolon in front of it. If you write them, you never meet the problem.

A second trap involves return followed by a line break: the language inserts a semicolon right after return, so the value on the next line is never returned. Keep the returned value on the same line as return. Functions and return are covered in the function forms lesson.

Comments

A comment is text that the engine ignores. It is for people.

// A line comment runs to the end of the line.
const rate = 0.2; // It can follow code.
/* A block comment can
span several lines. */

Block comments do not nest: the first */ ends the comment. Comments that begin with /** are documentation comments, and editors read them to show hints. Write comments that explain why, not what the line already says.

Naming

JavaScript does not enforce names, but everyone uses the same conventions:

Kind Style Example
Variables and functions camelCase totalPrice, readLine
Classes PascalCase ShoppingCart
Fixed constants UPPER_SNAKE_CASE MAX_RETRIES

Names are case-sensitive: total and Total are different variables. A name may contain letters, digits, _ and $, and cannot start with a digit.

Formatters and linters

A formatter such as Prettier rewrites your code into one consistent layout, so style stops being a discussion. A linter such as ESLint reads your code and reports likely mistakes, such as a variable that is never used. A later module sets both up. For now it is enough to know they exist and that most editors can run them when you save.

Strict mode

In its original form, JavaScript allowed things that were almost always mistakes, and it could not simply start rejecting them without breaking old websites. So ES5 introduced strict mode, an opt-in switch that turns some silent behaviour into errors.

Two cases are important to know:

  • Code in an ES module is always strict. Every .mjs file in this course is strict without you doing anything.
  • Code in other files is sloppy by default. To opt in, put the directive "use strict"; as the first statement of the file.

What strict mode changes, with examples that run in a module:

  • Assigning to a name that was never declared is a ReferenceError. Sloppy mode would create a global variable out of a typo.
total = 10;
console.log("never reached");
Output
ReferenceError: total is not defined
  • Writing to something that cannot be written is a TypeError. In sloppy mode, the assignment is ignored without a word. Frozen objects, which you meet in the objects lesson, are an example.
  • The with statement, an obsolete feature that made scope hard to reason about, is a SyntaxError.

A silent failure is far worse than a loud one, so strict mode is the right place to be. If you ever see an old tutorial that begins every file with "use strict";, now you know why, and why you do not need it in modules.

Common pitfalls

  • A line starting with ( or [ after a line without a semicolon. It is glued to the previous line.
  • A newline after return. The function returns undefined.
  • Inconsistent naming. Price_Tag, pricetag and priceTag look like three names. Pick camelCase and stay with it.
  • Misspelling a variable name in sloppy code. It silently creates a new global. In a module, you get a ReferenceError instead.
  • Putting "use strict" in the middle of a file. The directive only counts as the first statement.
ExerciseTidy a messy file

This file works, but it breaks the course’s style: an inconsistent name, no semicolons, and no explanation of what it does. Rewrite it with camelCase names, semicolons and a one-line comment at the top.

const Item_Count = 3
const unitprice = 4
const total = Item_Count * unitprice
console.log(`Total: ${total}`)
console.log(`With 10% off: ${total * 0.9}`)

Input: none. Expected output stays the same: Total: 12 then With 10% off: 10.8.

Show solution
tidy_file.mjs
// Prints a total and the total with 10% off.
const itemCount = 3;
const unitPrice = 4;
const total = itemCount * unitPrice;
console.log(`Total: ${total}`);
console.log(`With 10% off: ${total * 0.9}`);
Output
Total: 12
With 10% off: 10.8

The names follow camelCase, every statement ends with a semicolon, and the comment says what the file computes.

ExerciseWhy does it crash?

Save this file and run it. It stops with an error, although every line looks right.

const width = 6
const height = 7
const area = width * height
(width + height) * 2

Find the cause from the error message, then fix the file so that it prints the area, 42, and then the perimeter, 26.

Input: none. Expected output after the fix: 42 then 26.

Show solution

The error is TypeError: height is not a function. The last line starts with (, so the language reads width * height(width + height) * 2 as one statement and tries to call the number 7. Semicolons stop the lines joining. The last line is also a bare calculation whose result is thrown away, so the fixed program prints both values:

bracket_fixed.mjs
const width = 6;
const height = 7;
const area = width * height;
console.log(area);
console.log((width + height) * 2);
Output
42
26

Putting console.log( in front of the bracket gives the second calculation a statement of its own, which is the better fix for a line that was meant to do something.

ExerciseRead the comments

Work out what this program prints before you run it.

const a = 4; // const a = 5;
/* const b = 2; */ const b = 3;
// console.log("skipped");
console.log(a * b);
/* console.log("also skipped"); */

Input: none. Expected output: 12.

Show solution
read_comments.mjs
const a = 4; // const a = 5;
/* const b = 2; */ const b = 3;
// console.log("skipped");
console.log(a * b);
/* console.log("also skipped"); */
Output
12

The engine ignores everything after // on a line and everything between /* and */. Only const a = 4, const b = 3 and the one live console.log remain, so the result is 4 times 3.

Summary

  • An expression produces a value, and a statement does something. You can only use an expression where a value is expected.
  • Write semicolons, keep return values on the same line, and be wary of lines that start with ( or [.
  • Use camelCase for variables and functions, PascalCase for classes and UPPER_SNAKE_CASE for fixed constants.
  • Modules are always strict, so assigning to an undeclared name is an error. Other code can opt in with "use strict";.

Next, in the module on values and types: primitive types and typeof.