Skip to main content

JavaScript Classes

Introduction

JavaScript classes were introduced in ECMAScript 2015 (ES6) to provide a cleaner, more intuitive syntax for creating objects and implementing inheritance. Although JavaScript is primarily a prototype-based language, classes offer a more familiar syntax for developers coming from class-based object-oriented programming languages like Java or C++.

Classes in JavaScript are essentially "special functions" that serve as templates or blueprints for creating objects. They encapsulate data with code to work on that data, making your code more organized, reusable, and maintainable.

Class Declaration

You can define a JavaScript class using the class keyword, followed by the name of the class.

javascript
class Person {
// Class body
}

Let's create a simple Person class with properties and methods:

javascript
class Person {
constructor(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}

// Method to get full name
getFullName() {
return `${this.firstName} ${this.lastName}`;
}

// Method to check if person is adult
isAdult() {
return this.age >= 18;
}
}

// Creating an instance of the Person class
const john = new Person('John', 'Doe', 30);
console.log(john.getFullName()); // Output: John Doe
console.log(john.isAdult()); // Output: true

The Constructor Method

The constructor method is a special method for creating and initializing objects created with a class. There can only be one constructor method in a class.

The constructor method is called automatically when a new instance of the class is created using the new keyword, and it's where you initialize the properties of the object.

javascript
class Book {
constructor(title, author, year) {
this.title = title;
this.author = author;
this.year = year;
}
}

const myBook = new Book('1984', 'George Orwell', 1949);
console.log(myBook.title); // Output: 1984
console.log(myBook.author); // Output: George Orwell
console.log(myBook.year); // Output: 1949

Class Methods

Class methods are functions defined inside the class body. They are added to the prototype of the class, which means all instances of the class share the same method.

javascript
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}

// Method to calculate area
calculateArea() {
return this.width * this.height;
}

// Method to calculate perimeter
calculatePerimeter() {
return 2 * (this.width + this.height);
}
}

const rect = new Rectangle(5, 10);
console.log(rect.calculateArea()); // Output: 50
console.log(rect.calculatePerimeter()); // Output: 30

Static Methods

Static methods are called on the class itself, not on instances of the class. They're often used for utility functions related to the class.

javascript
class MathUtils {
static add(x, y) {
return x + y;
}

static subtract(x, y) {
return x - y;
}

static multiply(x, y) {
return x * y;
}
}

console.log(MathUtils.add(5, 3)); // Output: 8
console.log(MathUtils.subtract(5, 3)); // Output: 2
console.log(MathUtils.multiply(5, 3)); // Output: 15

Note that you cannot call a static method on an instance of the class:

javascript
const math = new MathUtils();
// This will throw an error:
// math.add(5, 3); // TypeError: math.add is not a function

Getter and Setter Methods

Classes also support getter and setter methods, which allow you to control how a property is accessed and modified.

javascript
class Temperature {
constructor(celsius) {
this._celsius = celsius;
}

// Getter for celsius
get celsius() {
return this._celsius;
}

// Setter for celsius
set celsius(value) {
if (value < -273.15) {
throw new Error("Temperature below absolute zero is not possible");
}
this._celsius = value;
}

// Getter for fahrenheit
get fahrenheit() {
return (this._celsius * 9/5) + 32;
}

// Setter for fahrenheit
set fahrenheit(value) {
this._celsius = (value - 32) * 5/9;
}
}

const temp = new Temperature(25);
console.log(temp.celsius); // Output: 25
console.log(temp.fahrenheit); // Output: 77

temp.celsius = 30;
console.log(temp.celsius); // Output: 30
console.log(temp.fahrenheit); // Output: 86

temp.fahrenheit = 68;
console.log(temp.celsius); // Output: 20
console.log(temp.fahrenheit); // Output: 68

Class Inheritance

One of the most powerful features of JavaScript classes is inheritance, which allows you to create a new class that inherits properties and methods from an existing class. This is accomplished using the extends keyword.

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

speak() {
console.log(`${this.name} makes a noise.`);
}
}

class Dog extends Animal {
constructor(name, breed) {
super(name); // Call the parent class constructor
this.breed = breed;
}

speak() {
console.log(`${this.name} barks!`);
}

getBreed() {
return this.breed;
}
}

const dog = new Dog('Rex', 'German Shepherd');
dog.speak(); // Output: Rex barks!
console.log(dog.name); // Output: Rex
console.log(dog.getBreed()); // Output: German Shepherd

The super Keyword

The super keyword is used to call functions on an object's parent. When used in a constructor, it calls the parent class's constructor.

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

introduce() {
return `My name is ${this.name} and I am ${this.age} years old.`;
}
}

class Student extends Person {
constructor(name, age, grade) {
super(name, age); // Call parent constructor
this.grade = grade;
}

introduce() {
return `${super.introduce()} I am in grade ${this.grade}.`;
}
}

const student = new Student('Alice', 15, 10);
console.log(student.introduce());
// Output: My name is Alice and I am 15 years old. I am in grade 10.

Private Class Fields

With modern JavaScript (from ES2019+), you can declare private class fields using the # prefix. Private fields are only accessible within the class itself.

javascript
class BankAccount {
#balance = 0; // Private field

constructor(owner) {
this.owner = owner;
}

deposit(amount) {
if (amount <= 0) {
throw new Error("Deposit amount must be positive");
}
this.#balance += amount;
return this.#balance;
}

withdraw(amount) {
if (amount <= 0) {
throw new Error("Withdrawal amount must be positive");
}
if (amount > this.#balance) {
throw new Error("Insufficient funds");
}
this.#balance -= amount;
return this.#balance;
}

getBalance() {
return this.#balance;
}
}

const account = new BankAccount("John Doe");
account.deposit(1000);
console.log(account.getBalance()); // Output: 1000
account.withdraw(500);
console.log(account.getBalance()); // Output: 500

// This would throw an error:
// console.log(account.#balance); // SyntaxError: Private field '#balance' must be declared in an enclosing class

Real-World Example: Building a To-Do List Manager

Let's apply what we've learned by creating a simple task management system:

javascript
class Task {
constructor(id, title, description = "", completed = false) {
this.id = id;
this.title = title;
this.description = description;
this.completed = completed;
this.createdAt = new Date();
this.completedAt = null;
}

markAsCompleted() {
this.completed = true;
this.completedAt = new Date();
}

markAsIncomplete() {
this.completed = false;
this.completedAt = null;
}

updateTitle(newTitle) {
this.title = newTitle;
}

updateDescription(newDescription) {
this.description = newDescription;
}
}

class TaskManager {
#tasks = [];
#nextId = 1;

createTask(title, description = "") {
const task = new Task(this.#nextId++, title, description);
this.#tasks.push(task);
return task;
}

deleteTask(id) {
const index = this.#tasks.findIndex(task => task.id === id);
if (index !== -1) {
this.#tasks.splice(index, 1);
return true;
}
return false;
}

getAllTasks() {
return [...this.#tasks]; // Return a copy of the tasks array
}

getCompletedTasks() {
return this.#tasks.filter(task => task.completed);
}

getIncompleteTasks() {
return this.#tasks.filter(task => !task.completed);
}

getTaskById(id) {
return this.#tasks.find(task => task.id === id);
}
}

// Usage example
const taskManager = new TaskManager();

// Create some tasks
const task1 = taskManager.createTask("Learn JavaScript Classes", "Study the basics of JS classes");
const task2 = taskManager.createTask("Build a project");
const task3 = taskManager.createTask("Practice coding");

// Mark a task as completed
task1.markAsCompleted();

// Update a task
task2.updateTitle("Build a TodoList app");
task2.updateDescription("Create a simple to-do list application using JavaScript classes");

// Get all tasks
console.log("All Tasks:", taskManager.getAllTasks());

// Get completed tasks
console.log("Completed Tasks:", taskManager.getCompletedTasks());

// Get incomplete tasks
console.log("Incomplete Tasks:", taskManager.getIncompleteTasks());

// Delete a task
taskManager.deleteTask(3);
console.log("After deletion:", taskManager.getAllTasks());

This example demonstrates a practical use case for classes in JavaScript, showing how we can create a structured system with clear responsibilities and relationships between different objects.

Summary

In this lesson, we covered JavaScript classes, a powerful feature for implementing object-oriented programming in JavaScript. Here's what we learned:

  • Class declaration and instantiation using the new keyword
  • Constructor method for initializing object properties
  • Creating instance methods that operate on object data
  • Static methods that are called on the class itself
  • Getters and setters for controlled property access
  • Class inheritance using extends and super
  • Private class fields for encapsulation
  • A real-world example of using classes to build a task management system

Classes provide a cleaner, more structured way to create objects and implement inheritance in JavaScript, making your code more organized, reusable, and maintainable.

Exercises

To reinforce your understanding of JavaScript classes, try these exercises:

  1. Create a Shape class with methods to calculate area and perimeter. Then create subclasses for specific shapes like Circle, Rectangle, and Triangle.

  2. Implement a Library class that manages a collection of Book objects. Include methods to add, remove, and search for books.

  3. Build a simple bank account system with different account types (checking, savings) that inherit from a base Account class.

  4. Create a User class with private fields for password storage and public methods for authentication.

  5. Implement a simple Calculator class with static methods for basic arithmetic operations and instance methods for maintaining calculation history.

Additional Resources

Understanding JavaScript classes is crucial for writing modern JavaScript code, especially for larger applications where organization and structure are important.



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)