Photo blog

Image credit to Boitumelo

Published on August 8, 2024

Understanding Classes in JavaScript

JavaScript, being a versatile and powerful language, has undergone significant evolution over the years. One of the most notable additions in recent times is the introduction of classes in ECMAScript 6 (ES6). Before ES6, JavaScript relied heavily on prototype-based inheritance for object-oriented programming (OOP). The introduction of classes provided a more familiar and syntactically cleaner way for developers to create objects and implement OOP principles. In this blog post, we will dive into the concept of classes in JavaScript, explore how to initialize them, and understand the distinction between private and public methods within a class.

What is a Class in JavaScript?

A class in JavaScript is essentially a blueprint for creating objects. It encapsulates data and functions that operate on that data. While JavaScript classes are syntactical sugar over the existing prototype-based inheritance, they offer a more intuitive and structured way to create and manage objects.

A class can contain properties (variables) and methods (functions) that define the behavior of the objects created from it. Here's a simple example of a class in JavaScript:

class Animal {
  constructor(name, species) {
    this.name = name;
    this.species = species;
  }

  describe() {
    return `${this.name} is a ${this.species}.`;
  }
}

const lion = new Animal('Leo', 'Lion');
console.log(lion.describe()); // Output: "Leo is a Lion."

In the example above, we define a class Animal with a constructor that initializes the object properties name and species. The describe method is a public method that returns a string describing the animal.

The constructor Method

The constructor method is a special method in JavaScript classes. It is automatically called when a new object is created from a class. The primary purpose of the constructor is to initialize the object's properties.

When defining a class, the constructor method is written like any other method, but it is unique in that it is invoked immediately upon the creation of an object. Let's take a closer look at the constructor method:

class Car {
  constructor(brand, model, year) {
    this.brand = brand;
    this.model = model;
    this.year = year;
  }

  displayInfo() {
    return `${this.year} ${this.brand} ${this.model}`;
  }
}

const myCar = new Car('Toyota', 'Corolla', 2020);
console.log(myCar.displayInfo()); // Output: "2020 Toyota Corolla"

In this example, the Car class has a constructor method that initializes three properties: brand, model, and year. When we create a new instance of Car using new Car('Toyota', 'Corolla', 2020), the constructor is called, and the properties are set accordingly.

Private vs. Public Methods in a Class

JavaScript classes can contain both private and public methods. Public methods are accessible from outside the class, while private methods are intended to be used only within the class.

Public Methods

Public methods are simply methods that are defined within a class and can be accessed and invoked on instances of that class. For example:

class Calculator {
  add(a, b) {
    return a + b;
  }

  subtract(a, b) {
    return a - b;
  }
}

const calc = new Calculator();
console.log(calc.add(5, 3)); // Output: 8
console.log(calc.subtract(5, 3)); // Output: 2

In the Calculator class, both add and subtract methods are public. They can be called directly on an instance of the Calculator class, as shown in the example.

Private Methods

Prior to the introduction of private methods in ECMAScript 2020 (ES11), developers often used naming conventions (e.g., prefixing method names with an underscore) or closures to simulate private methods. However, ES11 introduced a formal way to declare private methods using the # symbol.

Private methods are not accessible from outside the class, which helps in encapsulating the internal workings of a class and preventing unintended interactions. Here's an example:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  #calculateBirthYear() {
    return new Date().getFullYear() - this.age;
  }

  getDetails() {
    return `${this.name} was born in ${this.#calculateBirthYear()}.`;
  }
}

const john = new Person('John', 30);
console.log(john.getDetails()); // Output: "John was born in 1994."
// console.log(john.#calculateBirthYear()); // Error: Private field '#calculateBirthYear' must be declared in an enclosing class

In this example, the #calculateBirthYear method is a private method that cannot be accessed outside the Person class. It's only used internally by the getDetails method. If you try to access #calculateBirthYear directly from an instance of Person, it will throw an error.

Why Use Private Methods?

Private methods are useful when you want to hide the internal implementation details of a class and expose only the methods that are necessary for the external interface. This promotes better encapsulation and reduces the risk of unintended side effects from external code interacting with the internals of your class.

Static Methods

In addition to private and public methods, JavaScript classes also support static methods. Static methods are not tied to instances of the class; instead, they belong to the class itself. They are called on the class rather than on instances of the class. For example:

class MathHelper {
  static square(number) {
    return number * number;
  }
}

console.log(MathHelper.square(5)); // Output: 25

In this example, square is a static method of the MathHelper class. It can be called directly on the class using MathHelper.square(5), without needing to create an instance of the class.

Conclusion

Classes in JavaScript provide a robust and intuitive way to implement object-oriented programming principles. They allow developers to create blueprints for objects, encapsulate data, and define behavior in a structured manner. The constructor method is a key part of classes, enabling the initialization of objects when they are created. Furthermore, the distinction between public and private methods helps in managing access to the internal workings of a class, promoting better encapsulation and reducing the risk of unintended interactions.

As JavaScript continues to evolve, understanding and effectively utilizing classes will become increasingly important for developers. Whether you're building simple applications or complex systems, classes offer a powerful tool for organizing and managing your code in a more maintainable and scalable way.