Skip to main content
โšก Calmops

ES6 Classes: Syntax and Features

ES6 Classes: Syntax and Features

ES6 classes provide a cleaner syntax for creating objects and dealing with inheritance compared to prototype-based approaches.

Class Basics

Class Declaration

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    
    greet() {
        return `Hello, I'm ${this.name}`;
    }
}

const person = new Person("Alice", 30);
console.log(person.greet()); // "Hello, I'm Alice"

Class Expression

// Anonymous class
const Person = class {
    constructor(name) {
        this.name = name;
    }
};

// Named class expression
const Animal = class AnimalClass {
    constructor(name) {
        this.name = name;
    }
};

Constructor

The constructor method runs when creating a new instance:

class User {
    constructor(username, email) {
        this.username = username;
        this.email = email;
        this.createdAt = new Date();
    }
}

const user = new User("alice", "[email protected]");
console.log(user.createdAt); // Current date

Methods

Instance Methods

class Calculator {
    add(a, b) {
        return a + b;
    }
    
    subtract(a, b) {
        return a - b;
    }
    
    multiply(a, b) {
        return a * b;
    }
}

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

Static Methods

Static methods belong to the class, not instances:

class MathUtils {
    static add(a, b) {
        return a + b;
    }
    
    static multiply(a, b) {
        return a * b;
    }
}

console.log(MathUtils.add(5, 3)); // 8
console.log(MathUtils.multiply(5, 3)); // 15

const utils = new MathUtils();
console.log(utils.add(5, 3)); // TypeError

Getters and Setters

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    get fullName() {
        return `${this.firstName} ${this.lastName}`;
    }
    
    set fullName(name) {
        [this.firstName, this.lastName] = name.split(" ");
    }
}

const person = new Person("John", "Doe");
console.log(person.fullName); // "John Doe"

person.fullName = "Jane Smith";
console.log(person.firstName); // "Jane"
console.log(person.lastName); // "Smith"

Inheritance

Extends Keyword

class Animal {
    constructor(name) {
        this.name = name;
    }
    
    speak() {
        return `${this.name} makes a sound`;
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name);
        this.breed = breed;
    }
    
    speak() {
        return `${this.name} barks`;
    }
}

const dog = new Dog("Buddy", "Golden Retriever");
console.log(dog.speak()); // "Buddy barks"

Super Keyword

Call parent class methods:

class Vehicle {
    constructor(brand) {
        this.brand = brand;
    }
    
    start() {
        return `${this.brand} started`;
    }
}

class Car extends Vehicle {
    constructor(brand, model) {
        super(brand);
        this.model = model;
    }
    
    start() {
        const parentStart = super.start();
        return `${parentStart} - ${this.model} is ready`;
    }
}

const car = new Car("Toyota", "Camry");
console.log(car.start()); // "Toyota started - Camry is ready"

Private Fields

class BankAccount {
    #balance = 0; // Private field
    
    constructor(initialBalance) {
        this.#balance = initialBalance;
    }
    
    deposit(amount) {
        this.#balance += amount;
    }
    
    getBalance() {
        return this.#balance;
    }
}

const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
console.log(account.#balance); // SyntaxError - private field

Static Properties

class Counter {
    static count = 0;
    
    constructor() {
        Counter.count++;
    }
    
    static getCount() {
        return Counter.count;
    }
}

new Counter();
new Counter();
new Counter();

console.log(Counter.getCount()); // 3

Practical Examples

User Management System

class User {
    constructor(id, username, email) {
        this.id = id;
        this.username = username;
        this.email = email;
        this.createdAt = new Date();
    }
    
    getInfo() {
        return `${this.username} (${this.email})`;
    }
}

class Admin extends User {
    constructor(id, username, email, permissions = []) {
        super(id, username, email);
        this.permissions = permissions;
    }
    
    hasPermission(permission) {
        return this.permissions.includes(permission);
    }
    
    addPermission(permission) {
        if (!this.permissions.includes(permission)) {
            this.permissions.push(permission);
        }
    }
}

const admin = new Admin(1, "admin", "[email protected]", ["read", "write"]);
console.log(admin.getInfo()); // "admin ([email protected])"
console.log(admin.hasPermission("delete")); // false
admin.addPermission("delete");
console.log(admin.hasPermission("delete")); // true

Shape Hierarchy

class Shape {
    constructor(color) {
        this.color = color;
    }
    
    getArea() {
        throw new Error("getArea() must be implemented");
    }
    
    describe() {
        return `A ${this.color} shape`;
    }
}

class Circle extends Shape {
    constructor(color, radius) {
        super(color);
        this.radius = radius;
    }
    
    getArea() {
        return Math.PI * this.radius ** 2;
    }
    
    describe() {
        return `${super.describe()} with radius ${this.radius}`;
    }
}

class Rectangle extends Shape {
    constructor(color, width, height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    
    getArea() {
        return this.width * this.height;
    }
}

const circle = new Circle("red", 5);
console.log(circle.getArea()); // 78.54...
console.log(circle.describe()); // "A red shape with radius 5"

const rect = new Rectangle("blue", 10, 20);
console.log(rect.getArea()); // 200

Database Model

class Model {
    static #instances = [];
    
    constructor(data) {
        this.id = Date.now();
        this.createdAt = new Date();
        Object.assign(this, data);
        Model.#instances.push(this);
    }
    
    static findAll() {
        return Model.#instances;
    }
    
    static findById(id) {
        return Model.#instances.find(instance => instance.id === id);
    }
    
    save() {
        // Simulate saving
        return true;
    }
    
    delete() {
        const index = Model.#instances.indexOf(this);
        if (index > -1) {
            Model.#instances.splice(index, 1);
        }
    }
}

class User extends Model {
    constructor(username, email) {
        super({ username, email });
    }
}

const user1 = new User("alice", "[email protected]");
const user2 = new User("bob", "[email protected]");

console.log(Model.findAll().length); // 2
console.log(Model.findById(user1.id).username); // "alice"

Class vs Prototype

Prototype Approach

function Person(name) {
    this.name = name;
}

Person.prototype.greet = function() {
    return `Hello, ${this.name}`;
};

const person = new Person("Alice");

Class Approach

class Person {
    constructor(name) {
        this.name = name;
    }
    
    greet() {
        return `Hello, ${this.name}`;
    }
}

const person = new Person("Alice");

Both are equivalent, but classes are cleaner and more intuitive.

Best Practices

Use Meaningful Names

// Good
class UserRepository {
    findById(id) { }
}

// Avoid
class UR {
    fbi(id) { }
}

Keep Classes Focused

// Good - single responsibility
class User {
    constructor(name, email) {
        this.name = name;
        this.email = email;
    }
}

// Avoid - too many responsibilities
class User {
    constructor(name, email) {
        this.name = name;
        this.email = email;
    }
    
    sendEmail() { }
    validateEmail() { }
    saveToDatabase() { }
}

Use Private Fields for Encapsulation

// Good - encapsulation
class BankAccount {
    #balance = 0;
    
    deposit(amount) {
        this.#balance += amount;
    }
}

// Avoid - no encapsulation
class BankAccount {
    constructor() {
        this.balance = 0;
    }
}

Summary

  • Class: blueprint for creating objects
  • Constructor: initializes new instances
  • Methods: functions in a class
  • Static: methods/properties on class itself
  • Getters/Setters: special methods for properties
  • Inheritance: extend classes with extends
  • Super: call parent class methods
  • Private fields: encapsulation with #
  • Best practice: use classes for OOP

Official Documentation

Next Steps

  1. Inheritance: Extends and Super
  2. Static Methods and Properties
  3. Getters and Setters

Comments