← Back

CLASSES IN JAVASCRIPT

This note explains classes in simple language.

Classes are used when we need to create many similar objects with the same structure, the same properties, and the same methods, but with different values. Instead of creating each object manually, we create one class and then create many objects from it.

1. Why classes are needed

An object literal is useful when we want to create one object.

Example

const user = {
  name: "Aaron",
  email: "exemple@nikitagoluban.eu",
};

But in real programs we often need many similar objects:

If we create all of them manually, the code becomes repetitive. That is why classes are used.

Diagram 1. Why classes exist

One object
↓
object literal is enough

Many similar objects
↓
use a class

2. Declaring a class

A class is declared with:

Example

class User {
  // Class body
}

Class names usually begin with a capital letter. This helps us quickly see that this is a class.

Diagram 2. Class structure

class User {
  // body
}

class  → keyword
User   → class name
{ }    → class body

3. Creating an instance from a class

After a class is declared, we can create an object from it using the new operator. An object created from a class is called an instance.

Example

class User {}

const aaron = new User();
console.log(aaron); // {}

const benjamin = new User();
console.log(benjamin); // {}

Explanation

Here:

Diagram 3. Class and instances

class User
   ↓
new User()
   ↓
instance object

4. Constructor

A class uses a special method called constructor to initialize new objects. When we call new User(...), JavaScript automatically creates a new object and calls the constructor.

Example

class User {
  constructor(name, email) {
    console.log(name, email);
  }
}

const aaron = new User("Aaron", "exemple@nikitagoluban.eu");

The values passed into new User(...) become arguments for the constructor.

5. this inside the constructor

Inside the constructor, this refers to the object that is currently being created. That is why we use this to save values into the new object.

Example

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}

const aaron = new User("Aaron", "exemple@nikitagoluban.eu");
console.log(aaron); // { name: "Aaron", email: "exemple@nikitagoluban.eu" }

const benjamin = new User("Benjamin", "exemple2@nikitagoluban.eu");
console.log(benjamin); // { name: "Benjamin", email: "exemple2@nikitagoluban.eu" }

Diagram 4. Constructor work

new User("Aaron", "exemple@nikitagoluban.eu")
↓
constructor(name, email)
↓
this.name = "Aaron"
this.email = "exemple@nikitagoluban.eu"
↓
new object is ready

6. Public properties

Properties like name and email in the example above are called public properties. They belong directly to the object, and we can access them from outside the class.

Example

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}

const aaron = new User("Aaron", "exemple@nikitagoluban.eu");

console.log(aaron.name);  // "Aaron"
console.log(aaron.email); // "exemple@nikitagoluban.eu"

Diagram 5. Public properties

aaron
│
├─ name: "Aaron"
└─ email: "exemple@nikitagoluban.eu"

7. Parameter object pattern

Sometimes a class needs many values. Instead of passing many separate arguments, we can pass one object with named properties. This is called the parameter object pattern.

Example

class User {
  constructor(params) {
    this.name = params.name;
    this.email = params.email;
  }
}

const aaron = new User({
  name: "Aaron",
  email: "exemple@nikitagoluban.eu",
});

console.log(aaron);

Why this is useful

This style is easier to read because we immediately see what each value means.

Diagram 6. Parameter object

new User({
  name: "Aaron",
  email: "exemple@nikitagoluban.eu"
})

One object
↓
named values
↓
clearer constructor call

8. Class methods

A class can contain methods. These methods are used to work with the properties of the object.

Example

class User {
  constructor(params) {
    this.name = params.name;
    this.email = params.email;
  }

  getEmail() {
    return this.email;
  }

  changeEmail(newEmail) {
    this.email = newEmail;
  }
}

Explanation

Here:

Diagram 7. Class with methods

class User
│
├─ constructor(...)
├─ getEmail()
└─ changeEmail(newEmail)

9. Using class methods

After we create an instance, we can call its methods.

Example

const aaron = new User({
  name: "Aaron",
  email: "exemple@nikitagoluban.eu",
});

console.log(aaron.getEmail()); // "exemple@nikitagoluban.eu"

aaron.changeEmail("exemple2@nikitagoluban.eu");

console.log(aaron.getEmail()); // "exemple2@nikitagoluban.eu"

The methods work with the properties of the object that called them.

10. Where class methods are stored

A very important point:

Class methods are not copied directly into every object.

Instead, they are stored in:

User.prototype

and objects use them through the prototype chain.

Example

class User {
  constructor(params) {
    this.name = params.name;
    this.email = params.email;
  }

  getEmail() {
    return this.email;
  }

  changeEmail(newEmail) {
    this.email = newEmail;
  }
}

console.log(User.prototype);

This is efficient because there is no need to copy the same method into every instance. All instances share the same prototype methods.

Diagram 8. Class → prototype → instance

class User
   │
   ├─ constructor()
   ├─ getEmail()
   └─ changeEmail()
        │
        ▼
User.prototype
        │
        ▼
new User(...) → instance
                │
                ├─ name
                └─ email

11. Encapsulation

Encapsulation means hiding the internal details of a class from outside code. Outside code should use the public interface of the class, not change everything directly.

In JavaScript, encapsulation is done with private properties and private methods.

12. Private properties

A private property starts with #.

It can be used only inside the class. Outside code cannot read it directly.

Example

class User {
  name;
  #email;

  constructor(params) {
    this.name = params.name;
    this.#email = params.email;
  }
}

const aaron = new User({
  name: "Aaron",
  email: "exemple@nikitagoluban.eu",
});

console.log(aaron.name); // "Aaron"
// console.log(aaron.#email); // Error

Explanation

Diagram 9. Public vs private

User instance
│
├─ name      → public
└─ #email    → private

13. Accessing private data through public methods

Because private properties cannot be accessed directly, we usually create public methods to work with them.

Example

class User {
  name;
  #email;

  constructor(params) {
    this.name = params.name;
    this.#email = params.email;
  }

  getEmail() {
    return this.#email;
  }

  changeEmail(newEmail) {
    this.#email = newEmail;
  }
}

const aaron = new User({
  name: "Aaron",
  email: "exemple@nikitagoluban.eu",
});

console.log(aaron.getEmail()); // "exemple@nikitagoluban.eu"
aaron.changeEmail("exemple2@nikitagoluban.eu");
console.log(aaron.getEmail()); // "exemple2@nikitagoluban.eu"

Diagram 10. Working with private property

Outside code
   │
   ├─ getEmail()
   └─ changeEmail(...)
        │
        ▼
     #email

14. Private methods

A private method also starts with #.

It can only be called inside the class. This is useful when you want to hide inner logic.

Example

class User {
  name;
  #email;

  constructor(params) {
    this.name = params.name;
    this.#email = params.email;
  }

  getEmail() {
    return this.#email;
  }

  changeEmail(newEmail) {
    if (this.#validateEmail(newEmail)) {
      this.#email = newEmail;
    } else {
      console.log("Invalid email format");
    }
  }

  #validateEmail(email) {
    return email.includes("@");
  }
}

Explanation

Here:

Diagram 11. Private method

Public method:
changeEmail()

Inside it:
#validateEmail()

Outside code
↓
cannot call #validateEmail()

15. Getters and setters

Getters and setters are special methods that look like normal properties when we use them. They are convenient when we want controlled access to a value, especially a private one.

Example

class User {
  #email;

  constructor(params) {
    this.name = params.name;
    this.#email = params.email;
  }

  get email() {
    return this.#email;
  }

  set email(newEmail) {
    this.#email = newEmail;
  }
}

Explanation

Example of use

const aaron = new User({
  name: "Aaron",
  email: "exemple@nikitagoluban.eu",
});

console.log(aaron.email); // "exemple@nikitagoluban.eu"

aaron.email = "exemple2@nikitagoluban.eu";

console.log(aaron.email); // "exemple2@nikitagoluban.eu"

Diagram 12. Getter and setter

aaron.email
↓
getter runs

aaron.email = "exemple2@nikitagoluban.eu"
↓
setter runs

16. Why getters and setters are useful

Because they let us run extra logic while still using simple property syntax.

Example

set email(newEmail) {
  if (newEmail === "") {
    console.log("Error! Email cannot be an empty string!");
    return;
  }

  this.#email = newEmail;
}

Now the setter protects the class from invalid values.

17. Static properties

A class can have properties that belong to the class itself, not to instances. These are called static properties.

Example

class MyClass {
  static myProp = "value";
}

console.log(MyClass.myProp); // "value"

Important

Instances do not have access to static properties.

class MyClass {
  static myProp = "value";
}

const inst = new MyClass();
console.log(inst.myProp); // undefined

Diagram 13. Static property

MyClass.myProp     → works
instance.myProp    → undefined

18. Static property example with user roles

class User {
  static roles = {
    admin: "admin",
    editor: "editor",
    basic: "basic"
  };

  #email;
  #role;

  constructor(params) {
    this.#email = params.email;
    this.#role = params.role || User.roles.basic;
  }

  get role() {
    return this.#role;
  }

  set role(newRole) {
    this.#role = newRole;
  }
}

Here roles belongs to the class itself. It stores possible roles that can be used when creating objects.

19. Static methods

A class can also have methods that belong to the class itself. These are called static methods.

Example

class MyClass {
  static myMethod() {
    console.log("A static method");
  }
}

MyClass.myMethod();

Static methods are called on the class, not on instances.

20. Static method example

class User {
  static #takenEmails = [];

  static isEmailTaken(email) {
    return User.#takenEmails.includes(email);
  }

  #email;

  constructor(params) {
    this.#email = params.email;
    User.#takenEmails.push(params.email);
  }
}

const aaron = new User({ email: "exemple@nikitagoluban.eu" });

console.log(User.isEmailTaken("exemple2@nikitagoluban.eu"));  // false
console.log(User.isEmailTaken("exemple@nikitagoluban.eu")); // true

Explanation

Here:

Diagram 14. Static data and static method

User class
│
├─ static #takenEmails
└─ static isEmailTaken()

Instances
↓
do not access these directly

21. Inheritance

Inheritance allows one class to use the properties and methods of another class.

For this, JavaScript uses:

extends

Example

class Parent {}

class Child extends Parent {}

This means Child inherits from Parent.

Why inheritance is useful

It helps avoid repeating common code.

22. Example of inheritance with User and ContentEditor

class User {
  #email;

  constructor(email) {
    this.#email = email;
  }

  get email() {
    return this.#email;
  }

  set email(newEmail) {
    this.#email = newEmail;
  }
}

class ContentEditor extends User {}

const editor = new ContentEditor("exemple@nikitagoluban.eu");
console.log(editor.email); // "exemple@nikitagoluban.eu"

ContentEditor inherits from User, so it gets the email logic from the parent class.

23. Child class constructor and super()

If a child class has its own constructor, it must call:

super(...)

inside that constructor. super(...) calls the parent constructor.

Example

class User {
  #email;

  constructor(email) {
    this.#email = email;
  }

  get email() {
    return this.#email;
  }

  set email(newEmail) {
    this.#email = newEmail;
  }
}

class ContentEditor extends User {
  constructor(params) {
    super(params.email);
    this.posts = params.posts;
  }
}

const editor = new ContentEditor({
  email: "exemple@nikitagoluban.eu",
  posts: []
});

console.log(editor.email); // "exemple@nikitagoluban.eu"
console.log(editor.posts); // []

Explanation

Here:

Diagram 15. Child constructor

ContentEditor constructor
│
├─ super(params.email)
│   ↓
│   User constructor runs
│
└─ this.posts = params.posts

24. Child class methods

A child class can also add its own methods.

Example

class ContentEditor extends User {
  constructor(params) {
    super(params.email);
    this.posts = params.posts;
  }

  addPost(post) {
    this.posts.push(post);
  }
}

Example of use

const editor = new ContentEditor({
  email: "exemple@nikitagoluban.eu",
  posts: []
});

editor.addPost("post-1");
editor.addPost("post-2");

console.log(editor.posts); // ["post-1", "post-2"]

Explanation

Here:

Diagram 16. Inheritance structure

User
 │
 ├─ #email
 ├─ get email()
 └─ set email()
      │
      ▼
ContentEditor extends User
 │
 ├─ posts
 └─ addPost()

25. Easy memory rules

Class = blueprint
Instance = real object created from the class
Constructor = special method for creating the object
Public property = accessible from outside
Private property = hidden inside class
Getter/Setter = controlled access to a value
Static = belongs to the class, not to instances
Inheritance = child class reuses parent logic

26. Quick summary

27. Final conclusion

Classes help organize code in a clear and powerful way.

If you understand these ideas:

Class
Constructor
Methods
Public and private data
Getters and setters
Static properties and methods
Inheritance

then you already have a strong foundation for object-oriented programming in JavaScript.

← Back