Lexical Structure

This section documents modern ECMAScript and core browser JavaScript APIs — it is not tied to any specific framework or library (React, Vue, Angular, etc.). This content was generated with the assistance of AI and should be verified against the current ECMAScript specification and MDN documentation before relying on it in production, since JavaScript language features and browser API support continue to evolve.

This section’s bibliography lists the reference material consulted while preparing these pages.

Before writing any real JavaScript, it helps to understand the language’s lexical structure — the lowest-level rules that govern how source text is broken into tokens: what a valid variable name looks like, how comments are delimited, how statements are separated, and which characters are allowed where. None of this is about program behavior; it is about the mechanical shape every JavaScript file must have before a parser can even begin to make sense of it. The authoritative, exhaustive version of these rules lives in the specification itself, at ECMA-262 — Lexical Grammar; this page covers the parts of it that matter day to day.

Case Sensitivity, Whitespace, and Line Breaks

JavaScript is case-sensitive throughout: keywords, variable names, function names, and every other identifier must be typed with consistent capitalization. The while keyword only works spelled exactly while — not While or WHILE — and total, Total, and TOTAL are three unrelated identifiers as far as the language is concerned.

Outside of that, JavaScript is largely whitespace-insensitive. Spaces, tabs, and line breaks between tokens are ignored by the parser (with one notable exception — see Automatic Semicolon Insertion (ASI) below), which is why you are free to indent and format code however makes it most readable without changing what it does.

Comments

JavaScript supports the two comment styles common to C-family languages:

// A single-line comment: everything from // to the end of the line is ignored.

/* A block comment.
   It can span multiple lines, but block comments cannot be nested inside
   one another -- the first closing */ ends the whole comment.

const x = 1; // A comment can also trail real code on the same line.

Block comments (/* …​ /) not nesting is a common source of confusion when trying to comment out a block of code that already contains a block comment — the outer comment ends at the *first */ it finds, potentially leaving the rest of the code live.

Literals

A literal is a value written directly into the source code, as opposed to one computed at runtime. JavaScript’s basic literal forms are:

42;              // a number
3.14;            // also a number -- JavaScript has no separate integer type
"hello";         // a string, in double quotes
'hello';         // a string, in single quotes -- functionally identical
true;            // a boolean
false;           // a boolean
null;             // the deliberate absence of a value

Numbers, strings, arrays, and objects each have richer literal syntax of their own (numeric separators, template literals, array/object literal shorthand, and so on); this page only establishes that literals exist as a lexical category. See the rest of this reference for the details of each type’s own syntax.

Identifiers and Reserved Words

An identifier is a name — used for variables, constants, function names, class names, and loop labels. A legal JavaScript identifier must start with a letter, an underscore (_), or a dollar sign ($); every character after the first may additionally be a digit. Identifiers cannot start with a digit, which is what lets the parser tell an identifier apart from a number at a glance:

let i;
let _privateCounter;
let $element;
let camelCase2;

A set of words is reserved for the language itself and cannot be used as identifiers, including break, case, catch, class, const, continue, debugger, default, delete, do, else, export, extends, false, finally, for, function, if, import, in, instanceof, new, null, return, static, super, switch, this, throw, true, try, typeof, var, void, while, with, and yield, plus the newer async and await. A handful of other words — enum, implements, interface, package, private, protected, and public — are also off-limits, reserved for possible future use by the language even though nothing currently uses them.

A few words such as let, of, get, set, and from are only contextually reserved: they act as keywords in specific positions (let x = 1;, for (const x of arr)) but remain legal identifiers elsewhere for backward compatibility with code written before they took on special meaning. arguments and eval are not themselves reserved words, but are restricted from use as identifiers in certain contexts and are best avoided as variable names entirely. When in doubt, simply avoid naming anything after a word that appears anywhere in the grammar — it costs nothing and sidesteps every edge case.

Declaring Variables and Constants

Modern JavaScript declares bindings with let (a reassignable variable) or const (a binding that cannot be reassigned after its initial value is set):

let count = 0;
count = count + 1;       // fine -- let allows reassignment

const MAX_RETRIES = 3;
MAX_RETRIES = 5;         // TypeError -- const cannot be reassigned

Both keywords declare identifiers that are lexically scoped to the nearest enclosing block ({ …​ }), and both are subject to the language’s "temporal dead zone" rule: referencing the identifier before its own let/const line has executed throws a ReferenceError, rather than silently evaluating to undefined.

JavaScript also has an older declaration keyword, var, with function-level (rather than block-level) scoping and no temporal dead zone protection — it is hoisted and initialized to undefined for the entire enclosing function. var still appears throughout older code and libraries, so recognizing it is necessary, but new code should prefer let and const. See Legacy Features to Avoid for the full contrast and the scoping pitfalls var introduces.

Automatic Semicolon Insertion (ASI)

Statements are normally terminated with a semicolon. JavaScript, however, will insert a semicolon on your behalf at the end of a line when the token that follows cannot be parsed as a continuation of the current statement — a feature usually called Automatic Semicolon Insertion, or ASI. This is why the following two lines run as two separate statements even without an explicit ;:

let a = 3
let b = 4

ASI is a fallback, not a line-break-to-semicolon translation: JavaScript only inserts a semicolon when it genuinely cannot continue parsing the statement without one. That distinction produces some easy-to-miss bugs. A line beginning with (, [, ` , +, or - can often be read as a continuation of the statement above it:

let y = x + f
(a + b).toString()

// ASI does *not* kick in here -- this parses as a single statement:
let y = x + f(a + b).toString();

A defensive leading semicolon on any line that starts with ( or [ avoids this class of bug entirely, and is common enough in some style guides to be worth recognizing on sight:

let x = 0
;[x, x + 1, x + 2].forEach(v => console.log(v));

There are also three narrower exceptions where ASI is more aggressive than the general rule above, because certain constructs are only ever valid with nothing following them on the same line:

  • return, throw, break, and continue always get a semicolon inserted immediately if a line break follows the keyword, even when a valid continuation exists on the next line. return, followed by a line break, then true; on its own line, is parsed as return; true; — silently returning undefined — not as return true;. Never split one of these keywords from its operand across a line break.

  • Postfix ++ and -- must appear on the same line as the expression they operate on, or ASI inserts a semicolon before them.

  • An arrow function’s must appear on the same line as its parameter list.

Given how easy it is to trip over these edge cases, many style guides (and linters) sidestep the issue entirely by requiring an explicit semicolon at the end of every statement rather than relying on ASI at all; that convention is followed throughout this reference.

Unicode

JavaScript source text is Unicode, not just ASCII. Strings and comments may contain any Unicode character directly, and identifiers may contain Unicode letters, digits, and ideographs — though notably not emoji:

const π = 3.14159;
const 総数 = 42;

For portability, most real-world code sticks to ASCII identifiers by convention even though the language permits more, since not every editor, terminal, or collaborator renders non-ASCII characters reliably.

Unicode Escape Sequences

Where a non-ASCII character can’t be typed or displayed directly, JavaScript accepts a \u escape in string literals, regular expression literals, and identifiers (though never inside a keyword): either exactly four hex digits, or one to six hex digits wrapped in braces, the latter needed for characters outside the Basic Multilingual Plane such as most emoji:

let café = 1;
café;             // refers to the same binding as `café`
caf\u{e9};        // an equivalent escape, using the brace form

console.log("\u{1F600}");   // prints an emoji via its escape sequence

Normalization

Unicode allows more than one byte sequence to represent what looks like the same character — for example, é can be a single precomposed codepoint or an e followed by a separate combining accent mark. These render identically in an editor but are different sequences of codepoints, and JavaScript treats them as different identifiers:

const café = 1;          // "café" using the precomposed é (U+00E9)
const café = 2;    // a *different* identifier: "cafe" + combining accent (U+0301) -- renders
                          // identically to `café` above but is a distinct binding

JavaScript does not normalize source text on its own — it assumes the file it is given is already in a consistent normal form. Any editor or toolchain that touches source files containing non-ASCII identifiers should normalize them consistently to avoid creating visually indistinguishable but distinct bindings like the pair above.