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

StyleUsed forExample
camelCasevariables, functions, methods, propertiesretryCount, getUser()
PascalCaseclasses and anything you call with newclass UserAccount {}
SCREAMING_SNAKEfixed configuration values at module levelconst MAX_RETRIES = 3;
_leadingby convention "internal, do not touch". Not enforced_cache
#namegenuinely 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 modeIn strict mode
Assigning to an undeclared name creates a globalReferenceError
Writing to a read-only or frozen property fails silentlyTypeError
this in a plain function call is the global objectthis is undefined
Duplicate parameter names are allowedSyntaxError
delete someVariable fails quietlySyntaxError
Octal literals like 0755 are acceptedSyntaxError (use 0o755)
with is availableSyntaxError
'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 — total was never declared. In sloppy mode this would quietly create a global and log 3. Strict mode throws a ReferenceError instead, 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 with new". 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. #field is the enforced version.
  • Naming a variable data, info, obj or temp and 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.