Syntax, semicolons, comments and strict mode
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.
const result = 5 + 3;console.log(result * 2);16Here 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" }; // SyntaxErrorJavaScript 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:
const a = 1const b = 2const total = a + bconsole.log(total)3This 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 = 20const tax = 5const 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 functionWith semicolons, each line stands alone and the intent is clear:
const price = 20;const tax = 5;const total = price + tax;
console.log(total);25If 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
.mjsfile 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");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
withstatement, an obsolete feature that made scope hard to reason about, is aSyntaxError.
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 returnsundefined. - Inconsistent naming.
Price_Tag,pricetagandpriceTaglook 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
ReferenceErrorinstead. - Putting
"use strict"in the middle of a file. The directive only counts as the first statement.
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 = 3const unitprice = 4const total = Item_Count * unitpriceconsole.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
// 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}`);Total: 12With 10% off: 10.8The names follow camelCase, every statement ends with a semicolon, and the comment says what the file computes.
Save this file and run it. It stops with an error, although every line looks right.
const width = 6const height = 7const area = width * height(width + height) * 2Find 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:
const width = 6;const height = 7;const area = width * height;
console.log(area);console.log((width + height) * 2);4226Putting 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.
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
const a = 4; // const a = 5;/* const b = 2; */ const b = 3;// console.log("skipped");console.log(a * b);/* console.log("also skipped"); */12The 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
returnvalues 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.