A class creates a constructor function and its prototype object, with syntax for methods, fields and inheritance (ES2015, with fields, private names and static blocks in ES2022; ECMA-262, Class Definitions). The code in a class is always strict.
Declaration
ClassDeclaration:
class BindingIdentifier ClassTail
ClassExpression:
class [BindingIdentifier] ClassTail
ClassTail:
[ClassHeritage] { [ClassBody] }
ClassHeritage:
extends LeftHandSideExpression
ClassElement:
MethodDefinition
static MethodDefinition
FieldDefinition ;
static FieldDefinition ;
ClassStaticBlock
;
FieldDefinition:
ClassElementName [Initializer]
ClassElementName:
PropertyName
PrivateIdentifier
ClassStaticBlock:
static { ClassStaticBlockBody }
class Rectangle {
static count = 0; // static field
#area = 0; // private field
width = 1; // public field
constructor(width, height) {
this.width = width;
this.height = height;
this.#area = width * height;
Rectangle.count++;
}
get area() { return this.#area; } // accessor on the prototype
scale(k) { return new Rectangle(this.width * k, this.height * k); }
static unit() { return new Rectangle(1, 1); }
}
- A class declaration is hoisted but stays in its dead zone until evaluated, so it cannot be used earlier in the scope.
- Calling a class without
newthrows aTypeError. - Inside the class, the name is a read-only binding. Outside, a declaration’s
binding behaves like
let. - Methods are non-enumerable, and defined on the prototype, or on the constructor if
static. - A class is a function.
typeofgives"function", andtoStringreturns its source text.
Constructors
A class has at most one method named constructor. If there is none, a
default is supplied, and a derived class’s default passes all arguments to
super. A constructor may not be a getter, setter,
generator or async method.
A base class constructor returns this, unless it returns an object. A
derived constructor must not return a primitive other than undefined.
Fields
A field declares a property of each instance, or of the class if
static.
- Fields are defined rather than assigned, using
DefineField. A field therefore does not call a setter inherited from a parent class. - The initialiser is evaluated once for each instance, with
thisas the instance. A field with no initialiser isundefined. - Instance fields are added in order: in a base class before the constructor body
runs, and in a derived class straight after
super()returns. - Computed field names are evaluated once, when the class is defined.
argumentsmay not be used in an initialiser.- Field declarations end with a semicolon, usually left to automatic insertion.
Private names
A name starting with # makes a field, method or accessor private to the
class body. Private elements are not properties, so Object.keys and
reflection do not see them, and a Proxy does not forward them.
class Counter {
#n = 0;
static #instances = 0;
#step() { this.#n++; }
get value() { return this.#n; }
bump() { this.#step(); return this; }
static isCounter(x) { return #n in x; }
}
- A private name must be declared once in the class body. Using an undeclared one is a syntax error.
#constructoris not allowed.- Reading a private element from an object that does not have it throws a
TypeError.#x in objtests for it (ES2022). - A nested class can see the outer class’s private names.
- A private method is shared and cannot be written to.
this.#m = …throws aTypeError. - Private elements cannot be reached with a computed key, and cannot be
deleted.
Static blocks
class Config {
static values;
static {
this.values = load(); // this is the class
}
}
A static block runs once when the class is defined, in order with the static
fields (ES2022). It may use private names. await and
arguments are forbidden, and a return is a syntax error. A
class may have any number of them. Each has its own scope.
Inheritance
extends takes any expression that gives a constructor or
null.
class Shape {
constructor(name) { this.name = name; }
describe() { return `a ${this.name}`; }
static create() { return new this('shape'); }
}
class Circle extends Shape {
constructor(r) { super('circle'); this.r = r; }
describe() { return super.describe() + ` of radius ${this.r}`; }
}
Circle.create(); // inherited static; this is Circle
The prototypes are linked twice. Circle.prototype’s prototype is
Shape.prototype, for instances, and Circle’s prototype is
Shape, for statics. Built-ins such as Array and
Error can be subclassed. Symbol.species controls which
constructor their methods use for results.
super
| Form | Meaning | Where |
|---|---|---|
super(args) | Calls the parent constructor and binds this | A derived constructor |
super.x, super.m() | Reads from the parent’s prototype, or the parent class in static code. A method gets this as the current object | Any method, including in object literals |
- In a derived constructor,
thisis in a dead zone untilsuper()returns, and using it earlier throws aReferenceError. Callingsuper()twice also throws. superis bound statically through the method’s home object, so copying a method to another object does not change it.- Arrow functions see the enclosing
super.
Extending null
class C extends null has no parent constructor. Instances do not
inherit from Object.prototype, and constructing one fails unless the
constructor returns an object, since there is nothing for super to
call.
Order of evaluation
When a class definition is evaluated, these happen in order:
- The
extendsexpression. - Each computed key, in the order written, and methods are placed on the prototype or constructor.
- Static fields and static blocks, in the order written.
Instance fields wait until each instance is constructed.
Decorators
Decorators (@dec) are a TC39 proposal and are not part of ECMA-262.
Tools such as TypeScript and Babel implement them separately.