JavaScript Keywords Reference
A keyword is a word that the JavaScript engine treats as part of the language’s grammar rather than as a name you can choose yourself. Words like const, function, if, and class are reserved so that the parser can always tell, unambiguously, whether it is looking at your code’s structure or at a name you invented. Understanding exactly which words are reserved — and which only behave specially in certain positions — saves you from confusing SyntaxErrors and helps you read other people’s code more fluently.
Overview: How Keywords Work
Before the JavaScript engine can execute your code, it must tokenize it — break the raw text into a stream of tokens such as identifiers, punctuation, literals, and keywords — and then parse that stream into an Abstract Syntax Tree (AST) that describes the program’s structure. Keywords are special-cased during this process: when the tokenizer sees the characters f-o-r as a standalone word, it does not treat them as a variable name; it emits a for keyword token, and the parser then expects the specific grammar that follows a for statement (a parenthesized initializer, condition, and increment, or a for...of / for...in header).
This is why you cannot write const for = 5; — the parser reads for as the start of a loop construct, not as an identifier, and then chokes on the unexpected =. Reserved words are essentially off-limits as variable names, function names, labels, or property names used without quotes in certain positions (property names are actually exempt in modern JS — { class: 'Physics' } is legal because object keys are not identifiers in the same sense).
Not All Keywords Are Reserved Equally
JavaScript’s keyword list isn’t one flat list — it is layered, for historical and design reasons:
- Always-reserved keywords — illegal as identifiers in every context (e.g.
if,class,return). - Strict-mode-only reserved words — legal identifiers in old-style "sloppy mode" code, but reserved in strict mode, classes, and modules (e.g.
let,static,implements). Since ES6 modules and class bodies are always strict, these are effectively reserved in almost all modern code. - Future reserved words — not used by the language yet but held in reserve so a future spec can add them without breaking old code (e.g.
enum). - Contextual keywords — words like
async,await,of,get,set, andfromthat only mean something special in a specific syntactic position. Outside that position, they are perfectly ordinary identifiers. - Reserved literals —
true,false, andnullare technically keywords representing fixed values, not identifiers you can redefine.
Syntax: The Keyword Categories
There is no single "syntax" for a keyword the way there is for a loop or a function — instead, each keyword slots into a specific grammatical position. The table below groups the full ES2015+ keyword set by category so you can see at a glance which words are always off-limits and which only matter in context.
| Category | Keywords | Notes |
|---|---|---|
| Control flow | if, else, switch, case, default, for, while, do, break, continue |
Structure branching and loops. |
| Functions & classes | function, return, class, extends, super, new, this |
Define and invoke reusable code. |
| Declarations | var, const, let*, static* |
*Reserved only in strict mode / modules / classes. |
| Error handling | try, catch, finally, throw, debugger |
debugger triggers a breakpoint if devtools are open. |
| Operators as words | typeof, instanceof, in, void, delete |
Word-form operators, not punctuation. |
| Modules | import, export, from*, as* |
*from / as are contextual. |
| Async & generators | async*, await*, yield* |
Contextual, but reserved inside their own function bodies. |
| Reserved literals | true, false, null |
Fixed values, not assignable identifiers. |
| Future reserved | enum |
Unused today, saved for the language’s future. |
| Strict-mode reserved | implements, interface, package, private, protected, public |
Reserved because a type-annotation proposal may one day use them. |
Examples
Example 1: Core Keywords Working Together
This example uses several always-reserved keywords — function, const, if, typeof, throw, try, and catch — in one small program.
function calculateArea(radius) {
const pi = 3.14159;
if (typeof radius !== "number") {
throw new TypeError("radius must be a number");
}
return pi * radius * radius;
}
try {
console.log(calculateArea(4));
console.log(calculateArea("oops"));
} catch (error) {
console.log(`Caught an error: ${error.message}`);
}
Output:
50.26544
Caught an error: radius must be a number
typeof checks the runtime type of radius and returns the string "string" for the second call, so the if body runs and throw raises a TypeError. Because that call is wrapped in try, the thrown error is caught rather than crashing the program.
Example 2: Classes, Inheritance, and this/super
class, extends, super, static, new, and this are the backbone of JavaScript’s class syntax.
class Shape {
static description = "A generic shape";
constructor(name) {
this.name = name;
}
describe() {
return `${this.name} is a shape.`;
}
}
class Circle extends Shape {
constructor(radius) {
super("Circle");
this.radius = radius;
}
describe() {
return `${super.describe()} Its radius is ${this.radius}.`;
}
}
const circle = new Circle(5);
console.log(circle.describe());
console.log(Shape.description);
Output:
Circle is a shape. Its radius is 5.
A generic shape
super("Circle") calls the parent constructor and sets this.name; super.describe() then calls the parent’s version of the method before Circle‘s own describe appends more text. static attaches description to the class itself, not to instances.
Example 3: Contextual Keywords in Action
async, await, and of only carry special meaning where the grammar expects them; get and set only mean something inside an object or class literal.
const account = {
_balance: 100,
get balance() {
return `$${this._balance}`;
},
set balance(value) {
if (value < 0) {
throw new RangeError("Balance cannot be negative");
}
this._balance = value;
}
};
async function processTransactions(amounts) {
for (const amount of amounts) {
account.balance = account._balance + amount;
console.log(`New balance: ${account.balance}`);
}
return account.balance;
}
async function run() {
const final = await processTransactions([50, -20, 10]);
console.log(`Final balance: ${final}`);
}
run();
Output:
New balance: $150
New balance: $130
New balance: $140
Final balance: $140
The for...of loop iterates the values of the amounts array. Each assignment to account.balance silently calls the set balance accessor, and each read calls get balance. Because processTransactions contains no await of its own, it runs to completion synchronously before run's await ever suspends — that suspension only delays the final log to the next microtask turn.
Under the Hood: Generators and yield
yield is one of the trickiest keywords because it is reserved inside a generator function body but behaves as an ordinary identifier outside one. Calling .next() on a generator object resumes the function's execution from wherever it last paused, right up to the next yield (or to the end of the function).
function* countUpTo(max) {
console.log("Generator started");
for (let current = 1; current <= max; current++) {
yield current;
}
console.log("Generator finished");
}
const counter = countUpTo(3);
console.log(counter.next());
console.log(counter.next());
console.log(counter.next());
console.log(counter.next());
Output:
Generator started
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
Generator finished
{ value: undefined, done: true }
Calling countUpTo(3) does not run any code yet — it just creates a generator object. Each .next() call resumes execution up to the next yield, packaging the yielded value into { value, done: false }. Once the loop ends and the function body finishes, the final .next() call returns { value: undefined, done: true }, signaling that the generator is exhausted.
Common Mistakes
Mistake 1: Using a Reserved Word as a Variable Name
Always-reserved keywords can never be identifiers, no matter the context:
const class = "Math";
console.log(class);
This throws SyntaxError: Unexpected token 'class' because the parser expects a class declaration after the keyword class, not an assignment. The fix is simply to pick a different name:
const className = "Math";
console.log(className);
Output:
Math
Mistake 2: Using yield Outside a Generator
Because yield is only reserved inside generator function bodies, writing it inside a regular function produces a confusing parse error rather than doing what you might expect:
function getNumbers() {
yield 1;
yield 2;
}
This fails with SyntaxError: Unexpected number, because outside a generator, yield is parsed as an ordinary identifier, and an identifier immediately followed by a number literal with no operator between them is not valid JavaScript. The fix is to mark the function as a generator with function*:
function* getNumbers() {
yield 1;
yield 2;
}
const gen = getNumbers();
console.log(gen.next().value);
console.log(gen.next().value);
Output:
1
2
Mistake 3: Confusing for...in with for...of
Both use the word for, but in and of produce very different iterations. for...in walks the enumerable property keys of an object (for arrays, that means index strings), while for...of walks the values produced by an iterable.
const colors = ["red", "green", "blue"];
console.log("Using for...in:");
for (const key in colors) {
console.log(key);
}
console.log("Using for...of:");
for (const value of colors) {
console.log(value);
}
Output:
Using for...in:
0
1
2
Using for...of:
red
green
blue
Using for...in on an array is a classic mistake: it gives you index strings ("0", "1", "2") instead of the actual values, and it will also pick up any custom enumerable properties added to the array or its prototype. Reach for for...of whenever you actually want the values.
Best Practices
- Never try to name a variable, function, or class after a reserved word — even strict-mode-only ones like
let,static, oryield, since your code may run in strict mode anyway (all ES module and class code does). - Treat contextual keywords like
async,await,of,get,set, andfromwith caution as identifiers — they are technically legal as variable names in most positions, but using them invites confusion for readers. - Prefer
constandletovervarso that variable scope follows the block structure your keywords already imply, avoiding the classicvarhoisting surprises. - Use
async/awaitinstead of raw.then()chains for asynchronous code — it reads top-to-bottom and pairs naturally withtry/catchfor error handling. - Reserve
function*andyieldfor genuine lazy-iteration or custom-iterator use cases; for straightforward loops, plain functions andfor...ofare easier to follow. - When in doubt about whether a word is safe to use as an identifier, just avoid it — a descriptive alternative name is always available and never risks a
SyntaxError.
Practice Exercises
- Exercise 1: Write a function named
describePersonthat usestypeofto check whether its argument is a string, and usesthrow/try/catchto handle the case where it is not. - Exercise 2: Write a
class Animalwith aspeakmethod, and aclass Dog extends Animalthat overridesspeakbut also callssuper.speak()inside it. Log the result of callingspeakon aDoginstance. - Exercise 3: Write a generator function
evenNumbers(limit)thatyields only even numbers from 0 up tolimit. Use afor...ofloop to print every value it produces.
Summary
- Keywords are words the JavaScript parser reserves for its own grammar, so they cannot be used as identifiers in the positions where they carry meaning.
- Always-reserved keywords (like
if,class,return) are off-limits everywhere; strict-mode-only words (likelet,static) are off-limits in modern module and class code. - Contextual keywords (
async,await,of,get,set,from,as) only take on special meaning in specific syntactic positions. yieldis reserved only inside generator function bodies, which is a frequent source of confusing syntax errors.for...initerates keys;for...ofiterates values — mixing them up is one of the most common practical keyword mistakes.- When unsure whether a word is safe to use as a variable name, choose a different, more descriptive name instead.
