A class as an expression const A = class {}
Yes, a class in JavaScript is an expression, so const A = class {} is perfectly legal and creates the same class as an ordinary declaration. The only differences are how the class enters scope and whether it has a name visible from the outside.
Theory
TL;DR
- A class can be declared with a declaration,
class User {}, or with an expression,const User = class {}. - A class expression can be anonymous or named; the name of a named one is visible only inside the class body.
- Class expressions are what you need for dynamic class selection, class factories and classes local to a function.
- Neither a declaration nor an expression can be used before its declaration: both sit in the temporal dead zone.
- A class can be created and used inline right away:
new (class { ... })().
Quick example
// Class declaration
class UserA {
greet() { console.log("Hello!"); }
}
// Class expression
const UserB = class {
greet() { console.log("Hello!"); }
};
new UserA().greet(); // Hello!
new UserB().greet(); // Hello!Both forms create a class, the difference is only in where and how it is declared.
Anonymous and named expressions
A class expression can be anonymous or named.
Anonymous:
const User = class {
greet() {
console.log("Hello!");
}
};Named:
const User = class MyUser {
greet() {
console.log(MyUser.name); // "MyUser"
}
};
const u = new User();
u.greet(); // MyUserThe name
MyUseris visible only inside the class itself, from the outside only the variableUseris available.
console.log(typeof MyUser); // ReferenceErrorThis mirrors named function expressions: the inner name is handy for recursive references and readable stack traces, but it does not create an outer variable.
Where this is useful
1. When the class has to be created dynamically. For example, depending on a condition:
const Animal = condition
? class Dog {
bark() { console.log("Woof!"); }
}
: class Cat {
meow() { console.log("Meow!"); }
};
const pet = new Animal();2. When a class has to be returned from a function (a class factory).
function createModel(name) {
return class {
constructor(id) {
this.id = id;
this.modelName = name;
}
};
}
const UserModel = createModel("User");
const user = new UserModel(1);
console.log(user.modelName); // "User"Very convenient for building models, entities, adapters and so on dynamically. The same trick is the basis of mixins: a function takes a base class and returns
class extends Base { ... }.
3. When the class should be local, inside a function.
function makeCounter() {
return class {
#count = 0;
inc() { this.#count++; return this.#count; }
};
}
const Counter = makeCounter();
const c = new Counter();
console.log(c.inc()); // 1The class lives only inside the function and does not pollute the global scope.
Hoisting behaviour
A class declaration is not hoisted the way a function is: the class name is reserved, but until the declaration line it stays in the temporal dead zone. A class expression behaves like any const variable, that is, it is available only after the declaration.
new User(); // ReferenceError: Cannot access 'User' before initialization
const User = class {};So, as far as hoisting goes, both forms are the same in practice: you cannot use a class before it is declared.
A class as an inline value
Since a class is an expression, it can be created and applied straight away without ever being named:
console.log(
new (class {
sayHi() {
console.log("Hello!");
}
})()
); // an instance of a temporary classThis trick is often used in tests and demos, so that no separate class name has to be introduced.
Summary table
| Type | Example | Where the name is available | Hoisted? |
|---|---|---|---|
| Class Declaration | class User {} | Everywhere after the declaration | No |
| Anonymous Class Expression | const A = class {} | There is no name | No |
| Named Class Expression | const A = class MyA {} | Only inside the class | No |
In short: a class can be used as an expression, which is convenient for dynamic, local and temporary classes, and the class name in that form can be local or absent entirely.
Common mistakes
- Thinking the name of a named class expression is visible outside.
const A = class MyA {}does not create aMyAvariable in the outer scope. - Using the class before its declaration line. That is a
ReferenceErrorbecause of the temporal dead zone, for declarations too. - Forgetting the semicolon after the expression.
const A = class {}ends with an expression, so a missing;can make the next line continue it unexpectedly. - Getting
A.namewrong for an anonymous class. Inconst A = class {}the engine infers the name from the variable, soA.namegives"A", not an empty string. - Creating classes in a hot loop. A class factory builds a new class with a new prototype on every call, so instances from different calls fail
instanceofagainst each other. - Expecting a class expression to escape strict mode. A class body is always strict, whatever the declaration form.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.