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.
class Person {
// Class body
}
Let's create a simple Person
class with properties and methods:
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.
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.
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.
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:
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.
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.
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.
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.
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:
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
andsuper
- 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:
-
Create a
Shape
class with methods to calculate area and perimeter. Then create subclasses for specific shapes likeCircle
,Rectangle
, andTriangle
. -
Implement a
Library
class that manages a collection ofBook
objects. Include methods to add, remove, and search for books. -
Build a simple bank account system with different account types (checking, savings) that inherit from a base
Account
class. -
Create a
User
class with private fields for password storage and public methods for authentication. -
Implement a simple
Calculator
class with static methods for basic arithmetic operations and instance methods for maintaining calculation history.
Additional Resources
- MDN Web Docs: Classes
- JavaScript.info: Classes
- You Don't Know JS: this & Object Prototypes
- Eloquent JavaScript: Object-Oriented Programming
- Clean Code concepts adapted for JavaScript
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! :)