Skip to main content

Classes in JavaScript

A class in JavaScript is syntactic sugar over a constructor function and prototypes. From the outside it is a declarative description of an object type, while under the hood everything works exactly as before: methods land on prototype, and new creates a new object and binds this to it. Such code simply looks and reads much better.

Theory

TL;DR

  • class is syntactic sugar over a constructor function and prototypes, not a new inheritance model.
  • constructor() runs on new, and methods from the class body land on prototype, so they are shared by every instance.
  • extends links the prototypes of two classes, and super() calls the parent constructor.
  • static makes a method or property a member of the class itself rather than of an instance.
  • # fields are private and reachable only inside the class body (ES2022), while get / set control how properties are read and written.
  • Classes are not hoisted the way functions are, and their methods are non-enumerable.

Quick example

javascript
class User { constructor(name, age) { this.name = name; this.age = age; } sayHi() { console.log(`Hi, my name is ${this.name}`); } } const user1 = new User('Alex', 25); user1.sayHi(); // Hi, my name is Alex
  • class declares a class.
  • constructor() is the special method invoked by new User().
  • this is a reference to the object being created.
  • Class methods automatically end up on prototype.

What it looks like under the hood

This code:

javascript
class User { constructor(name) { this.name = name; } sayHi() { console.log(this.name); } }

is equivalent to the old ES5 code:

javascript
function User(name) { this.name = name; } User.prototype.sayHi = function() { console.log(this.name); };

The difference is not in the model but in the behaviour details: a class body always runs in strict mode, class methods are non-enumerable, so for...in over an instance will not see them, and a class cannot be called without new at all.

Parts of a class

FeatureDescription
constructor()The method that runs when an object is created (new)
class methodsAdded to prototype and available to every instance
extendsInheritance from another class
super()A call to the parent constructor
staticStatic methods and properties that belong to the class itself
private fields (#)Fields that are unreachable outside the class
getters and settersLet you control how properties are read and written

A class can be declared not only as a statement but also as an expression (class expression):

javascript
const User = class { sayHi() { console.log('Hi!'); } }; new User().sayHi(); // Hi!

Or it can be given a name that is visible only inside the definition itself:

javascript
const User = class MyUser { getClassName() { return MyUser.name; } };

Inheritance: extends and super

javascript
class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a sound.`); } } class Dog extends Animal { constructor(name, breed) { super(name); // calls the parent constructor this.breed = breed; } speak() { console.log(`${this.name} barks!`); } } const rex = new Dog('Rex', 'shepherd'); rex.speak(); // Rex barks!
  • extends links the prototypes of the classes.
  • super() calls the parent constructor, and in a derived class it must run before the first use of this.
  • Methods can be overridden, just like in classic OOP.

static, private fields and accessors

Static methods and properties belong to the class itself, not to its instances:

javascript
class MathUtil { static add(a, b) { return a + b; } } console.log(MathUtil.add(2, 3)); // 5

Private fields and methods (#) were added in ES2022:

javascript
class Account { #balance = 0; // private field deposit(amount) { this.#balance += amount; } getBalance() { return this.#balance; } } const acc = new Account(); acc.deposit(100); console.log(acc.getBalance()); // 100 console.log(acc.#balance); // error: private property

# fields are reachable only inside the class.

Getters and setters let you control how a property is read and changed:

javascript
class User { constructor(name) { this._name = name; } get name() { return this._name.toUpperCase(); } set name(value) { this._name = value.trim(); } } const user = new User(' tim '); console.log(user.name); // TIM user.name = ' Alex '; console.log(user.name); // ALEX

Class versus constructor function

CriterionConstructor functionClass
SyntaxOld, verboseModern, declarative
new requiredYesYes
MethodsThrough prototypeInside the class body
this behaviourCan be unpredictableClear and standard
extends supportManualBuilt in
Private fieldsNoneYes (through #)

A short syntax recap:

ConceptWhat it does
classDeclares a class
constructorThe method invoked by new
extendsInheritance
super()A call to the parent constructor
staticMethods of the class, not of an instance
#fieldA private property
get / setControl over property access

Common mistakes

  1. Assuming classes are hoisted the way functions are. A class declaration lands in the temporal dead zone, so touching it above the class line throws:

    javascript
    const user = new User(); // ReferenceError class User {}
  2. Calling a class without new. User('Alex') throws TypeError: Class constructor User cannot be invoked without 'new', while an old constructor function in the same call would quietly pollute the global object.

  3. Touching this before calling super(). In a derived class this does not exist until the parent constructor has run, so you get a ReferenceError.

  4. Expecting class methods in for...in. They are non-enumerable, unlike properties assigned to a prototype by hand.

  5. Passing a method as a callback and losing this. setTimeout(user.sayHi, 0) calls the method unbound; you need user.sayHi.bind(user), an arrow wrapper, or a class field holding an arrow function.

  6. Confusing #balance with the _balance naming convention. The underscore is only an agreement between developers, while a # field really is unreachable from outside: referencing it outside the class body is a syntax error.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.