Day 8: Classes and Modules
What You'll Learn Today
- Basic class syntax and instantiation
- Constructors and methods
- Inheritance (extends, super)
- Static methods and properties
- Module system (import/export)
What Is a Class?
A class is a blueprint (template) for creating objects. It was introduced in ES6.
flowchart TB
subgraph Class["Class: Person"]
C1["Properties: name, age"]
C2["Methods: greet()"]
end
Class -->|"new"| I1["Instance 1<br/>name: 'John'"]
Class -->|"new"| I2["Instance 2<br/>name: 'Jane'"]
Class -->|"new"| I3["Instance 3<br/>name: 'Bob'"]
style Class fill:#3b82f6,color:#fff
style I1 fill:#22c55e,color:#fff
style I2 fill:#22c55e,color:#fff
style I3 fill:#22c55e,color:#fff
Basic Class Syntax
class Person {
// Constructor: called when creating an instance
constructor(name, age) {
this.name = name;
this.age = age;
}
// Method
greet() {
console.log(`Hello, I'm ${this.name}`);
}
// Method
introduce() {
console.log(`${this.name}, ${this.age} years old`);
}
}
// Creating instances
const person1 = new Person("John", 25);
const person2 = new Person("Jane", 30);
person1.greet(); // "Hello, I'm John"
person2.introduce(); // "Jane, 30 years old"
console.log(person1.name); // "John"
console.log(person2.age); // 30
Getters and Setters
Customize access to properties.
class Rectangle {
constructor(width, height) {
this._width = width;
this._height = height;
}
// Getters
get width() {
return this._width;
}
get height() {
return this._height;
}
get area() {
return this._width * this._height;
}
// Setters
set width(value) {
if (value <= 0) throw new Error("Width must be positive");
this._width = value;
}
set height(value) {
if (value <= 0) throw new Error("Height must be positive");
this._height = value;
}
}
const rect = new Rectangle(10, 5);
console.log(rect.area); // 50 (access like property, not method call)
rect.width = 20;
console.log(rect.area); // 100
// rect.width = -5; // Error: Width must be positive
Static Methods and Properties
Methods and properties that belong to the class itself, not instances.
class MathUtils {
static PI = 3.14159;
static add(a, b) {
return a + b;
}
static multiply(a, b) {
return a * b;
}
static circleArea(radius) {
return MathUtils.PI * radius ** 2;
}
}
// Call directly from class (no instance needed)
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.add(5, 3)); // 8
console.log(MathUtils.circleArea(5)); // 78.53975
// Cannot access from instance
// const utils = new MathUtils();
// utils.add(5, 3); // Error
Inheritance
Extend existing classes to create new ones.
flowchart TB
subgraph Parent["Parent Class: Animal"]
P1["name"]
P2["speak()"]
end
subgraph Child1["Child Class: Dog"]
C1["breed"]
C2["speak() - override"]
C3["fetch()"]
end
subgraph Child2["Child Class: Cat"]
C4["color"]
C5["speak() - override"]
C6["climb()"]
end
Parent --> Child1
Parent --> Child2
style Parent fill:#3b82f6,color:#fff
style Child1 fill:#22c55e,color:#fff
style Child2 fill:#f59e0b,color:#000
// Parent class
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
eat() {
console.log(`${this.name} eats`);
}
}
// Child class
class Dog extends Animal {
constructor(name, breed) {
super(name); // Call parent constructor
this.breed = breed;
}
// Override method
speak() {
console.log(`${this.name} barks`);
}
// New method
fetch() {
console.log(`${this.name} fetches the ball`);
}
}
class Cat extends Animal {
speak() {
console.log(`${this.name} meows`);
}
climb() {
console.log(`${this.name} climbs a tree`);
}
}
const dog = new Dog("Buddy", "Golden Retriever");
const cat = new Cat("Whiskers");
dog.speak(); // "Buddy barks"
dog.eat(); // "Buddy eats" (inherited from parent)
dog.fetch(); // "Buddy fetches the ball"
cat.speak(); // "Whiskers meows"
cat.climb(); // "Whiskers climbs a tree"
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
The super Keyword
class Employee {
constructor(name, salary) {
this.name = name;
this.salary = salary;
}
getInfo() {
return `${this.name}: $${this.salary}`;
}
}
class Manager extends Employee {
constructor(name, salary, department) {
super(name, salary); // Call parent constructor
this.department = department;
}
getInfo() {
// Call parent method and extend
return `${super.getInfo()} (${this.department} dept)`;
}
}
const manager = new Manager("Alice", 75000, "Engineering");
console.log(manager.getInfo()); // "Alice: $75000 (Engineering dept)"
Private Fields (ES2022+)
Fields starting with # cannot be accessed outside the class.
class BankAccount {
#balance = 0; // Private field
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
}
}
withdraw(amount) {
if (amount > 0 && amount <= this.#balance) {
this.#balance -= amount;
return amount;
}
return 0;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// console.log(account.#balance); // SyntaxError: Private field
Module System
Organize code across multiple files.
Export
// math.js
// Named exports
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// Export class
export class Calculator {
add(a, b) { return a + b; }
subtract(a, b) { return a - b; }
}
// Default export (one per file)
export default function subtract(a, b) {
return a - b;
}
Import
// app.js
// Named imports
import { PI, add, multiply } from './math.js';
console.log(PI); // 3.14159
console.log(add(5, 3)); // 8
// Import with alias
import { add as sum } from './math.js';
console.log(sum(5, 3)); // 8
// Import all
import * as MathUtils from './math.js';
console.log(MathUtils.PI);
console.log(MathUtils.multiply(4, 5));
// Default import
import subtract from './math.js';
console.log(subtract(10, 3)); // 7
// Default and named together
import subtract, { PI, add } from './math.js';
Module Structure Example
flowchart TB
subgraph App["app.js"]
A1["Main logic"]
end
subgraph Utils["utils/"]
U1["math.js"]
U2["string.js"]
U3["date.js"]
end
subgraph Models["models/"]
M1["User.js"]
M2["Product.js"]
end
App --> Utils
App --> Models
style App fill:#3b82f6,color:#fff
style Utils fill:#22c55e,color:#fff
style Models fill:#f59e0b,color:#000
Practical Example: User Management System
// User.js
export class User {
#password;
constructor(name, email, password) {
this.name = name;
this.email = email;
this.#password = password;
}
checkPassword(input) {
return this.#password === input;
}
getInfo() {
return `${this.name} <${this.email}>`;
}
}
// Admin.js
import { User } from './User.js';
export class Admin extends User {
static permissions = ['create', 'read', 'update', 'delete'];
constructor(name, email, password, department) {
super(name, email, password);
this.department = department;
this.role = 'admin';
}
getInfo() {
return `[Admin] ${super.getInfo()} - ${this.department}`;
}
hasPermission(action) {
return Admin.permissions.includes(action);
}
}
// app.js
import { User } from './User.js';
import { Admin } from './Admin.js';
const user = new User("John", "john@example.com", "secret123");
const admin = new Admin("Admin", "admin@example.com", "admin456", "IT");
console.log(user.getInfo()); // "John <john@example.com>"
console.log(admin.getInfo()); // "[Admin] Admin <admin@example.com> - IT"
console.log(admin.hasPermission('delete')); // true
Summary
| Concept | Description |
|---|---|
| class | Blueprint for objects |
| constructor | Method called when creating instance |
| this | Reference to the instance itself |
| extends | Inherit from a class |
| super | Reference to parent class |
| static | Members belonging to class itself |
| #field | Private field |
| export | Expose from module |
| import | Load module |
Key Takeaways
- Classes are templates for creating objects
- Inheritance for code reuse
- super to access parent class
- Private fields for encapsulation
- Modules to organize code
Exercises
Exercise 1: Basic Class
Create a Product class with name and price, and a getInfo() method that returns "Product name: $price".
Exercise 2: Inheritance
Extend Product to create a SaleProduct class with a discount property. Add a getFinalPrice() method that returns the discounted price.
Exercise 3: Static Method
Create a Counter class with a static method getCount() that returns the total number of instances created.
Challenge
Create a Shape class (abstract parent) and Circle, Rectangle classes, each with a getArea() method that calculates the area.
References
Coming Up Next: In Day 9, we'll learn about "Asynchronous JavaScript." Master Promises and async/await to fetch data from APIs!