15

Classes ES6+

Programmation Orientée Objet Moderne

Les classes JavaScript sont du sucre syntaxique sur les prototypes, mais apportent une syntaxe claire et des fonctionnalités modernes comme les champs privés (#) et les propriétés statiques.

📘 Syntaxe de Base

class User {
    // Champs publics (ES2022)
    role = 'user';
    
    // Champ privé (préfixe #)
    #password;
    
    constructor(name, password) {
        this.name = name;           // Propriété publique
        this.#password = password;  // Propriété privée
    }
    
    // Méthode publique
    greet() {
        return `Hello, ${this.name}!`;
    }
    
    // Getter (accès comme propriété)
    get displayName() {
        return this.name.toUpperCase();
    }
    
    // Setter (modification comme propriété)
    set displayName(value) {
        this.name = value.toLowerCase();
    }
    
    // Méthode privée
    #validatePassword() {
        return this.#password.length >= 8;
    }
    
    // Méthode statique (appelée sur la classe, pas l'instance)
    static isValidName(name) {
        return name.length > 2;
    }
}

// Utilisation
const user = new User('Alice', 'secret123');
console.log(user.greet());           // "Hello, Alice!"
console.log(user.displayName);       // "ALICE" (getter)
user.displayName = 'Bob';            // Utilise le setter
console.log(user.name);              // "bob"

// console.log(user.#password);      // ❌ SyntaxError: Private field
console.log(User.isValidName('Al')); // false (static)

🔧 Fonctionnalités des Classes

PUBLIC Champs Publics
class Counter {
    count = 0;  // Initialisé
    
    increment() {
        this.count++;
    }
}
Accessibles de partout
PRIVATE Champs Privés
class BankAccount {
    #balance = 0;
    
    deposit(amount) {
        this.#balance += amount;
    }
}
Inaccessibles de l'extérieur
STATIC Méthodes Statiques
class Math2 {
    static add(a, b) {
        return a + b;
    }
}
Math2.add(2, 3); // 5
Appelées sur la classe
🔄 Getters / Setters
class Circle {
    #radius = 0;
    
    get area() {
        return Math.PI * this.#radius ** 2;
    }
}
Propriétés calculées

🧬 Héritage avec extends

Animal (Parent)
Dog (Enfant)
// Classe parente
class Animal {
    constructor(name) {
        this.name = name;
    }
    
    speak() {
        return `${this.name} makes a sound`;
    }
}

// Classe enfant (hérite de Animal)
class Dog extends Animal {
    constructor(name, breed) {
        super(name);  // ⚠️ Obligatoire : appelle le constructeur parent
        this.breed = breed;
    }
    
    // Override (surcharge) de la méthode parent
    speak() {
        return `${this.name} barks!`;
    }
    
    // Appeler la méthode parent
    speakLikeAnimal() {
        return super.speak();  // Appelle Animal.speak()
    }
}

const dog = new Dog('Rex', 'Labrador');
console.log(dog.speak());              // "Rex barks!"
console.log(dog.speakLikeAnimal());    // "Rex makes a sound"
console.log(dog instanceof Dog);       // true
console.log(dog instanceof Animal);    // true

🎮 Constructeur de Classe Interactif

Créez une classe et voyez le code généré.

Cliquez sur "Générer" pour voir le code

🎯 Patterns Avancés

// 1. SINGLETON PATTERN
class Database {
    static #instance = null;
    
    constructor() {
        if (Database.#instance) {
            return Database.#instance;
        }
        Database.#instance = this;
        this.connection = 'Connected';
    }
    
    static getInstance() {
        if (!Database.#instance) {
            Database.#instance = new Database();
        }
        return Database.#instance;
    }
}

const db1 = Database.getInstance();
const db2 = Database.getInstance();
console.log(db1 === db2);  // true (même instance)

// 2. FACTORY PATTERN
class User {
    constructor(name, role) {
        this.name = name;
        this.role = role;
    }
    
    static createAdmin(name) {
        return new User(name, 'admin');
    }
    
    static createGuest(name) {
        return new User(name, 'guest');
    }
}

const admin = User.createAdmin('Alice');
const guest = User.createGuest('Bob');

// 3. BUILDER PATTERN
class QueryBuilder {
    #query = '';
    
    select(fields) {
        this.#query += `SELECT ${fields} `;
        return this;  // Chaînage
    }
    
    from(table) {
        this.#query += `FROM ${table} `;
        return this;
    }
    
    where(condition) {
        this.#query += `WHERE ${condition}`;
        return this;
    }
    
    build() {
        return this.#query.trim();
    }
}

const query = new QueryBuilder()
    .select('*')
    .from('users')
    .where('age > 18')
    .build();
// "SELECT * FROM users WHERE age > 18"

⚖️ Classes vs Prototypes

// Ancienne méthode (Prototype)
function Person(name) {
    this.name = name;
}

Person.prototype.greet = function() {
    return 'Hello ' + this.name;
};

const p = new Person('Alice');
// Nouvelle méthode (Class)
class Person {
    constructor(name) {
        this.name = name;
    }
    
    greet() {
        return `Hello ${this.name}`;
    }
}

const p = new Person('Alice');
💡 Équivalence : Les classes sont du sucre syntaxique. Sous le capot, JavaScript utilise toujours les prototypes. Person.prototype existe même avec la syntaxe class.
⚠️ Pièges Courants :
  • super() obligatoire : Dans un constructeur enfant, super() doit être appelé avant d'utiliser this.
  • Champs privés vraiment privés : #field n'est pas accessible même via obj['#field']. C'est une erreur de syntaxe.
  • this dans les callbacks : Utilisez les arrow functions ou .bind(this) pour préserver le contexte.
  • Pas de hoisting : Les classes ne sont pas "hoisted". Déclarez-les avant utilisation.
Best Practice : Utilisez les champs privés (#) pour l'encapsulation. Préférez les méthodes statiques pour les utilitaires. Utilisez extends avec parcimonie (composition > héritage).
← Spread Suivant: Utilitaires →