Classes
Why it matters
Say there are a hundred enemies in a game. Each has a name, health, attack and the same actions: "hit", "take damage". Writing a hundred objects by hand takes forever. A class is a blueprint: you can create as many objects of one kind from it as you like. Such an object is called an instance of the class.
Many libraries are built on classes, and the built-in Map, Set, Date, Error are classes too.
Declaring a class
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hi, I'm ${this.name}`;
}
}
const anna = new User("Anna", 15);
const timur = new User("Timur", 17);
console.log(anna.greet());
console.log(timur.age);
Output:
Hi, I'm Anna
17
class User { … }— a class declaration. Class names start with a capital letter.constructor— a special method called when an object is created. Properties are set in it.this— the object being created.this.name = nameputs the value into itsnameproperty.new User(…)— create an instance. Withoutnewyou get an error.- Methods (
greet) are written inside the class without the wordfunctionand without commas between them.
Methods change state
class Counter {
constructor() {
this.value = 0;
}
increase(step = 1) {
this.value += step;
return this.value;
}
}
const c = new Counter();
c.increase();
c.increase(5);
console.log(c.value);
Output:
6
Each instance keeps its own properties: two different Counters count independently.
Getters and setters
A getter is a method read like a property, without parentheses. A setter is a method that runs on assignment.
class Temperature {
constructor(celsius) {
this.celsius = celsius;
}
get fahrenheit() {
return this.celsius * 9 / 5 + 32;
}
set fahrenheit(f) {
this.celsius = (f - 32) * 5 / 9;
}
}
const t = new Temperature(25);
console.log(t.fahrenheit);
t.fahrenheit = 212;
console.log(t.celsius);
Output:
77
100
A getter is handy for computed values and for "read-only": if there's no setter, you can't assign a value.
Private fields
A field whose name starts with # is visible only inside the class:
class Wallet {
#money = 0;
add(x) { this.#money += x; }
get total() { return this.#money; }
}
const w = new Wallet();
w.add(50);
console.log(w.total);
Output:
50
Outside, w.#money is a syntax error. This protects data from accidental changes. The old-fashioned way
is to mark "internal" fields with an underscore (this._money) — just a convention.
Inheritance: extends and super
A class can inherit from another: get all its properties and methods and add its own.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound`;
}
sleep() {
return `${this.name} is sleeping`;
}
}
class Cat extends Animal {
speak() {
return `${this.name} meows`;
}
}
const kitty = new Cat("Kitty");
console.log(kitty.speak()); // its own method
console.log(kitty.sleep()); // inherited
console.log(kitty instanceof Cat, kitty instanceof Animal);
Output:
Kitty meows
Kitty is sleeping
true true
If the child class has its own constructor, it must first of all call super(...) — the parent's constructor:
class Student extends Animal {
constructor(name, school) {
super(name); // the parent sets this.name
this.school = school;
}
speak() {
return super.speak() + " and answers the lesson"; // calling the parent's method
}
}
console.log(new Student("Timur", "No. 5").speak());
Output:
Timur makes a sound and answers the lesson
Static methods
static is a method of the class itself, not of its instances: Math.max works in a similar way.
class MathUtil {
static square(x) { return x * x; }
}
console.log(MathUtil.square(7));
Output:
49
How it works inside
Class methods aren't copied into each object — they live in one shared place, the prototype
(User.prototype). When you call anna.greet(), JS first looks for greet in the object itself, doesn't
find it, and goes to the prototype. With inheritance, prototypes form a chain: Cat.prototype →
Animal.prototype → Object.prototype. The search goes along the chain until the method is found. That's
why kitty.sleep() works even though Cat doesn't have it.
Common mistakes
- Without
new.User("Anna")→TypeError: Class constructor User cannot be invoked without 'new'. - Without
this.name = namein the constructor doesn't save anything into the object. You needthis.name = name. - Forgot
super(). Usingthisin a child constructor beforesuper(...)is an error. - Commas between methods. In a class, methods are written without commas (unlike in an object).
- A getter with parentheses.
acc.balance()→ "is not a function" error. A getter is read without parentheses:acc.balance.
Cheat sheet
| What | How |
|---|---|
| Class | class Name { constructor(a) { this.a = a; } } |
| Instance | const x = new Name(1); |
| Method | go() { return this.a; } |
| Getter / setter | get total() {…}, set total(v) {…} |
| Private field | #secret = 0; |
| Inheritance | class B extends A { … } |
| Parent | super(…) in the constructor, super.method() |
| Static method | static create() {…} |
| Check | x instanceof Name |