Syntax, Style and Strict Mode
Mental model: Names are documentation, and strict mode turns silent mistakes into loud ones.
Level: beginner · about 9 minutes
An identifier is a name you invent: variables, functions, parameters, properties. The rules are short, and the conventions on top of them are stronger than the rules.
const userName = 'ada'; // letters, the usual case
const _internal = 1; // leading underscore is legal
const $el = 'button'; // so is a dollar sign
const cafe1 = 'digits are fine, just not first';
// const 2fast = 1; // SyntaxError: cannot start with a digit
// const class = 'x'; // SyntaxError: reserved word
// const my-name = 'x'; // SyntaxError: no hyphens, that is subtraction
console.log(userName, _internal, $el, cafe1);Legal names. The commented lines are SyntaxErrors.
Conventions the whole ecosystem shares
| Style | Used for | Example |
|---|---|---|
camelCase | variables, functions, methods, properties | retryCount, getUser() |
PascalCase | classes and anything you call with new | class UserAccount {} |
SCREAMING_SNAKE | fixed configuration values at module level | const MAX_RETRIES = 3; |
_leading | by convention "internal, do not touch". Not enforced | _cache |
#name | genuinely private class fields, enforced by the language | #balance |
Reserved words cannot be identifiers: break, case, catch, class, const, continue, debugger, default, delete, do, else, export, extends, finally, for, function, if, import, in, instanceof, new, return, super, switch, this, throw, try, typeof, var, void, while, with, yield. In strict mode implements, interface, let, package, private, protected, public and static join them.
Strict mode
JavaScript has two modes. Sloppy mode is the 1995 behaviour, kept because the web depends on it. Strict mode, added in ES5, turns a set of silent mistakes into thrown errors. You opt in with the string 'use strict' at the top of a file or a function.
const sloppy = new Function('mistake = 1; return typeof mistake;');
console.log(sloppy()); // → 'number'
console.log(typeof globalThis.mistake); // → 'number' a global appeared
const strict = new Function('"use strict"; oops = 1;');
try {
strict();
} catch (err) {
console.log(err.name); // → 'ReferenceError'
}Function bodies built by new Function are sloppy unless they opt in, which makes them a handy demo.
| In sloppy mode | In strict mode |
|---|---|
| Assigning to an undeclared name creates a global | ReferenceError |
| Writing to a read-only or frozen property fails silently | TypeError |
this in a plain function call is the global object | this is undefined |
| Duplicate parameter names are allowed | SyntaxError |
delete someVariable fails quietly | SyntaxError |
Octal literals like 0755 are accepted | SyntaxError (use 0o755) |
with is available | SyntaxError |
'use strict';
function whoAmI() {
return this;
}
console.log(whoAmI() === undefined);In a plain function call, strict mode leaves this as undefined instead of quietly substituting the global object. That is a feature: it turns "I forgot to bind this" into a visible TypeError on the next property access rather than a mutation of the global scope.
Formatting and linting, in two sentences
Prettier reformats your file to one consistent style on save, which ends every argument about semicolons and line width because nobody is choosing any more. ESLint reads your code for likely mistakes (unused variables, a missing await, an unreachable return) and is where team rules belong, since it judges meaning rather than layout.
Try it yourself
Rename for clarity
// before
const d = 3;
const arr = ['a@b.com', 'c@d.com'];
function proc(x) { return x.split('@')[1]; }
console.log(arr.map(proc), d);
// after
const MAX_RETRIES = 3;
const emailAddresses = ['a@b.com', 'c@d.com'];
const domainOf = (email) => email.split('@')[1];
console.log(emailAddresses.map(domainOf), MAX_RETRIES);
Rewrite the first block using the conventions from the table. Then check: can you read the second block without scrolling back up?
Exercises
camelCase to SCREAMING_SNAKE
Write toConstantCase(name) that converts a camelCase or PascalCase identifier into the constant convention: 'maxRetryCount' becomes 'MAX_RETRY_COUNT'. Insert an underscore at every lowercase-to-uppercase boundary, then uppercase everything. A name that is already a constant must come back unchanged.
Check yourself
- What does this log?
- a ReferenceError —
totalwas never declared. In sloppy mode this would quietly create a global and log3. Strict mode throws aReferenceErrorinstead, which is exactly the trade you want: a loud failure now beats a mysterious shared global later. - Which name follows the ecosystem conventions for a class?
UserAccount— PascalCase signals "call this withnew". camelCase is for variables and functions, SCREAMING_SNAKE for fixed config values, and a leading underscore is a hint that something is internal.- Where do you need to write
'use strict'? - Nowhere in an ES module or class body, because those are strict already — Modules and class bodies are strict by specification. You only need the directive in a classic script or an old CommonJS file, and even there a bundler usually adds it for you.
Common mistakes
- Adding
'use strict'halfway down a file. It only counts as the first statement of a file or a function body; anywhere else it is just a string. - Believing a leading underscore makes something private. It is a note to humans, nothing more.
#fieldis the enforced version. - Naming a variable
data,info,objortempand rediscovering what it holds every time you read the function.
Takeaways
- Identifiers may start with a letter,
_or$, never a digit, and never a reserved word. - camelCase for values and functions, PascalCase for classes, SCREAMING_SNAKE for fixed config.
- Strict mode converts silent failures (accidental globals, quiet write failures) into thrown errors.
- Modules and class bodies are already strict, so the directive is legacy maintenance.