JAVASCRIPT / VARIABLES: VAR, LET, AND CONST
Naming conventions and constant style
Pick JavaScript naming styles (camelCase, PascalCase, SCREAMING_SNAKE_CASE) that tell a reader what a binding is and whether its value is fixed.
What you will learn
- Use camelCase for variables and functions, PascalCase for classes and constructors
- Reserve SCREAMING_SNAKE_CASE for literals you typed by hand, not for every const
- Name booleans with is/has/can/should so conditions read as sentences
- Recognise illegal identifiers: leading digits, hyphens, and reserved words
Understanding Naming conventions and constant style
The engine enforces only a few naming rules: an identifier must start with a letter, `_`, or `$`, may not start with a digit, may not contain a hyphen (because `-` is already the subtraction operator), and may not be a reserved word like `class` or `new`. Everything past that is convention. `const maxRetries`, `const MaxRetries`, and `const max_retries` all run identically, so casing is a message to the next person reading the file rather than to the runtime. Plain JavaScript has no type annotations, so the name is often the only clue about what a binding holds and whether it is safe to touch.
The ecosystem has settled on three shapes: camelCase for variables, functions, and methods, matching the built-ins you already call (`parseInt`, `getElementById`); PascalCase for classes and constructor functions, so `new Sensor()` looks deliberate and the instance `sensor` is never confused with its class; and SCREAMING_SNAKE_CASE for a value fixed at the moment you typed it, such as a magic number lifted out of the code and given a name. The split that trips people up is that `const` is a rule about the binding while ALL_CAPS is a claim about the value. `const activeUsers = []` stays camelCase, because the array fills up even though the binding never moves. `MAX_RETRIES = 3` earns the shout because nothing about it depends on how the program runs.
Beyond casing, the shape of a name carries information. Values get nouns, functions get verbs, booleans get an is/has/can/should prefix so `if (isVerified)` reads as a sentence, collections get plurals, and ambiguous numbers get their unit baked in (`timeoutMs`, `sizeKb`) so no caller has to guess seconds versus milliseconds. Prefixes such as `_cache` for "internal" and `$row` for "this holds a DOM element" are pure signalling with nothing enforcing them, unlike `#count` in a class, which the engine genuinely makes private. Consistency inside one codebase matters more than any individually perfect name, which is why teams encode these rules in a linter (`camelcase`, `new-cap`, `id-length`) so they are checked instead of remembered.
const MAX_RETRIES = 3;
const REQUEST_TIMEOUT_MS = 5000;
const startedAt = Date.now();
const activeUsers = ["ada", "linus"];
function canRetry(attemptCount) {
return attemptCount < MAX_RETRIES;
}
activeUsers.push("grace");
console.log(canRetry(2), canRetry(3));
console.log(activeUsers.length, REQUEST_TIMEOUT_MS);
console.log(typeof startedAt);Casing in JavaScript is enforced by people rather than the engine, so camelCase, PascalCase, and SCREAMING_SNAKE_CASE are how you tell a reader what a binding is and whether its value was decided when the code was written.
Worked examples
Case sensitivity and legal characters
Two names that differ only in casing are two separate bindings, and `_` and `$` are ordinary identifier characters.
let userName = "Ada";
let username = "ada";
const _internalCache = {};
const $form = { id: "signup" };
console.log(userName === username);
console.log(userName, username);
console.log(Object.keys(_internalCache).length, $form.id);Example explained
Line 1`userName` and `username` differ by one letter's case, and identifiers are case sensitive, so the engine treats them as unrelated variables.
Line 2That is the practical argument for one convention: camelCase gives every multi-word name a single predictable spelling.
Line 3`_internalCache` and `$form` parse fine because `_` and `$` are legal identifier characters with no special meaning to the runtime.
Line 4The prefixes speak only to humans: "internal, leave it alone" and "this holds a DOM element".
Boolean and function names that read aloud
Prefixing booleans with is/has and predicates with should makes call sites self-describing.
const user = { name: "Grace", posts: [], verifiedAt: null };
const isVerified = user.verifiedAt !== null;
const hasPosts = user.posts.length > 0;
function shouldShowWelcome(isVerified, hasPosts) {
return isVerified && !hasPosts;
}
console.log(isVerified, hasPosts);
console.log(shouldShowWelcome(isVerified, hasPosts));
console.log(shouldShowWelcome(true, false));Example explained
Line 1`user.verifiedAt !== null` is false, and the name `isVerified` turns that bare false into a readable state.
Line 2`hasPosts` computes `user.posts.length > 0` once so no later condition has to repeat the comparison.
Line 3The parameters reuse the same names, so `shouldShowWelcome(isVerified, hasPosts)` needs no comment to explain argument order.
Line 4The `should` prefix marks the function as returning a decision, which is why `if (shouldShowWelcome(...))` reads as a sentence.
PascalCase for classes, camelCase for instances
Casing is the only thing distinguishing a constructor from one of its instances in a file.
class TemperatureSensor {
constructor(label) {
this.label = label;
this.readings = [];
}
record(celsius) {
this.readings.push(celsius);
return this;
}
get average() {
const total = this.readings.reduce((sum, n) => sum + n, 0);
return total / this.readings.length;
}
}
const CELSIUS_FREEZING_POINT = 0;
const roofSensor = new TemperatureSensor("roof");
roofSensor.record(4).record(-2).record(1);
console.log(roofSensor.label, roofSensor.average);
console.log(roofSensor.average > CELSIUS_FREEZING_POINT);Example explained
Line 1`TemperatureSensor` is PascalCase because it must be called with `new`; the capital letter is the only warning a reader gets that calling it plainly throws a TypeError.
Line 2`roofSensor` stays camelCase, so the class and an instance of it never look alike inside the same file.
Line 3`CELSIUS_FREEZING_POINT` is a literal decided while writing the code, which is the narrow case ALL_CAPS is meant for.
Line 4`average` is a getter, so it is named as a noun and read like a property: `roofSensor.average`, not `roofSensor.getAverage()`.
Important notes
Identifiers may legally contain Unicode letters and `\u` escapes, but stay with ASCII: names that look identical on screen can be separate bindings and are painful to search for.
A leading underscore is only a request. For enforced privacy in a class, `#count` is real syntax the engine checks; `_count` is not.
Common mistakes
Shouting every `const`: once a file contains `const CURRENT_USER = loadUser()` and `const TOTAL = items.length`, ALL_CAPS no longer marks anything, and the reader loses the only signal for values fixed at authoring time.
Assuming an ALL_CAPS name protects the contents: `const CONFIG = { debug: false }; CONFIG.debug = true;` succeeds, so anyone who trusted the name was misled; use `Object.freeze` when the guarantee is meant to be real.
Reaching for a hyphen or a leading digit: `let user-name = 1` and `let 2ndTry = 1` are both SyntaxErrors, so the whole file fails to parse and no line of it runs.
Try it yourself
Change, predict, then run
In a browser console, write a short cart total that uses an ALL_CAPS name for a hard-coded tax rate, camelCase names for the item array and the computed total, and a boolean called `hasItems`. Then rename every binding to a single letter and notice how much of the code you have to re-read to work out what it does.
Open the JavaScript workspaceCheck your understanding
Four bindings in one module are all declared with `const`. Which is the best candidate for SCREAMING_SNAKE_CASE by convention?
- `const currentUser = loadUser();` because `const` means the value can never change
- `const cart = [];` because it is const and will hold every item in the order
- `const MAX_UPLOAD_BYTES = 5 * 1024 * 1024;` a limit typed directly into the source
- `const Button = document.querySelector('#buy');` one element reused everywhere
Show answer
ALL_CAPS signals a value fixed when the code was written, which is exactly what `5 * 1024 * 1024` is. The first option is tempting because `const` blocks reassignment, but that is a rule about the binding, not evidence the value is a known constant: `loadUser()` returns something different on every run and per user, so it stays camelCase. The last option is wrong twice over, since PascalCase is reserved for things you call with `new`; that one should be `buyButton`.