JavaScript Avancé
ES6+ Mastery

Maîtrisez les concepts avancés de JavaScript moderne : ES6+, Async/Await, API REST, Modules, et techniques d'expert pour des applications complexes.

ES6+ Features
🔄 Async/Await
🌐 API REST
📦 Modules

⚡ ES6+ Features

Maîtrisez les fonctionnalités modernes de JavaScript qui révolutionnent le développement web.

🏹 Arrow Functions

Syntaxe concise pour les fonctions avec bind automatique du this.

// Fonction classique
function add(a, b) {
    return a + b;
}

// Arrow function
const add = (a, b) => a + b;

// Array methods avec arrow functions
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);

// This binding
class Counter {
    constructor() {
        this.count = 0;
    }
    
    // Arrow function préserve le this
    increment = () => {
        this.count++;
        console.log(this.count);
    }
}

📦 Destructuring

Extraction élégante des données depuis objets et tableaux.

// Destructuring d'objet
const user = { name: 'John', age: 30, email: 'john@example.com' };
const { name, age, email } = user;

// Avec renommage et valeurs par défaut
const { name: userName, country = 'France' } = user;

// Destructuring de tableau
const [first, second, ...rest] = [1, 2, 3, 4, 5];

// Destructuring en paramètres
function greet({ name, age }) {
    return `Bonjour ${name}, vous avez ${age} ans`;
}

// Destructuring imbriqué
const response = {
    data: {
        users: [{ name: 'Alice' }, { name: 'Bob' }]
    }
};
const { data: { users: [firstUser] } } = response;

📝 Template Literals

Chaînes de caractères avancées avec interpolation et multilignes.

const name = 'John';
const age = 30;

// Interpolation simple
const message = `Bonjour ${name}, vous avez ${age} ans`;

// Expressions complexes
const price = 99.99;
const formatted = `Prix: ${(price * 1.2).toFixed(2)}€`;

// Multilignes
const html = `
    

${name}

Age: ${age}

`; // Tagged template literals function highlight(strings, ...values) { return strings.reduce((result, string, i) => { const value = values[i] ? `${values[i]}` : ''; return result + string + value; }, ''); } const highlighted = highlight`Hello ${name}, welcome!`;

🎯 Spread & Rest

Opérateurs polyvalents pour manipulation de données.

// Spread operator avec tableaux
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];

// Spread avec objets
const defaults = { theme: 'dark', lang: 'fr' };
const userSettings = { lang: 'en', fontSize: 14 };
const config = { ...defaults, ...userSettings };

// Rest parameters
function sum(...numbers) {
    return numbers.reduce((total, n) => total + n, 0);
}

// Rest dans destructuring
const [first, ...remaining] = [1, 2, 3, 4, 5];
const { name, ...otherProps } = user;

// Copie superficielle
const userCopy = { ...user };
const arrayClone = [...originalArray];

🏛️ Classes ES6+

Syntaxe moderne pour la programmation orientée objet.

class User {
    // Propriétés privées
    #privateKey = 'secret';
    
    constructor(name, email) {
        this.name = name;
        this.email = email;
    }
    
    // Méthode getter
    get displayName() {
        return `User: ${this.name}`;
    }
    
    // Méthode setter
    set email(value) {
        if (value.includes('@')) {
            this._email = value;
        }
    }
    
    // Méthode statique
    static isValid(user) {
        return user.name && user.email;
    }
    
    // Méthode privée
    #validateKey() {
        return this.#privateKey === 'secret';
    }
}

// Héritage
class Admin extends User {
    constructor(name, email, permissions) {
        super(name, email);
        this.permissions = permissions;
    }
    
    hasPermission(action) {
        return this.permissions.includes(action);
    }
}

🔧 Nouvelles Méthodes

Méthodes avancées pour Array, Object, String et plus.

// Array methods
const users = [
    { name: 'Alice', age: 25, active: true },
    { name: 'Bob', age: 30, active: false },
    { name: 'Carol', age: 35, active: true }
];

// find/findIndex
const activeUser = users.find(u => u.active);
const index = users.findIndex(u => u.name === 'Bob');

// includes
const hasAlice = users.some(u => u.name === 'Alice');

// Object methods
const entries = Object.entries(users[0]);
const keys = Object.keys(users[0]);
const values = Object.values(users[0]);

// Object.assign
const merged = Object.assign({}, defaults, options);

// Optional chaining (ES2020)
const userName = user?.profile?.name ?? 'Anonymous';

// Nullish coalescing
const theme = settings.theme ?? 'default';

🔄 Async/Await Mastery

Maîtrisez la programmation asynchrone moderne avec Promises, async/await et gestion d'erreurs.

🔗 Promises Avancées

Fondement de la programmation asynchrone en JavaScript.

// Création d'une Promise
function fetchUser(id) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (id > 0) {
                resolve({ id, name: `User ${id}` });
            } else {
                reject(new Error('Invalid ID'));
            }
        }, 1000);
    });
}

// Chaînage de Promises
fetchUser(1)
    .then(user => {
        console.log('User:', user);
        return fetchUser(2);
    })
    .then(secondUser => {
        console.log('Second user:', secondUser);
    })
    .catch(error => {
        console.error('Error:', error.message);
    })
    .finally(() => {
        console.log('Operation completed');
    });

// Promise.all - Exécution parallèle
Promise.all([
    fetchUser(1),
    fetchUser(2),
    fetchUser(3)
]).then(users => {
    console.log('All users:', users);
});

⚡ Async/Await

Syntaxe synchrone pour code asynchrone - plus lisible et maintenable.

// Async function
async function getUser(id) {
    try {
        const user = await fetchUser(id);
        console.log('User:', user);
        return user;
    } catch (error) {
        console.error('Error:', error.message);
        throw error;
    }
}

// Appel séquentiel
async function getMultipleUsers() {
    try {
        const user1 = await fetchUser(1);
        const user2 = await fetchUser(2);
        return [user1, user2];
    } catch (error) {
        console.error('Failed to fetch users:', error);
    }
}

// Appel parallèle avec await
async function getAllUsersParallel() {
    try {
        const users = await Promise.all([
            fetchUser(1),
            fetchUser(2),
            fetchUser(3)
        ]);
        return users;
    } catch (error) {
        console.error('Some requests failed:', error);
    }
}

🚨 Gestion d'Erreurs

Stratégies robustes pour gérer les erreurs asynchrones.

// Try/catch avec async/await
async function robustFetch(url) {
    let retries = 3;
    
    while (retries > 0) {
        try {
            const response = await fetch(url);
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}`);
            }
            
            return await response.json();
        } catch (error) {
            retries--;
            
            if (retries === 0) {
                console.error('Max retries reached:', error);
                throw error;
            }
            
            // Attendre avant de réessayer
            await new Promise(resolve => 
                setTimeout(resolve, 1000)
            );
        }
    }
}

// Promise.allSettled - Tolérance aux erreurs
async function fetchAllUsers(ids) {
    const results = await Promise.allSettled(
        ids.map(id => fetchUser(id))
    );
    
    const successful = results
        .filter(r => r.status === 'fulfilled')
        .map(r => r.value);
        
    const failed = results
        .filter(r => r.status === 'rejected')
        .map(r => r.reason);
        
    return { successful, failed };
}

🎯 Patterns Avancés

Techniques d'expert pour l'asynchrone complexe.

// Async Generator
async function* fetchPages(baseUrl) {
    let page = 1;
    let hasMore = true;
    
    while (hasMore) {
        const response = await fetch(`${baseUrl}?page=${page}`);
        const data = await response.json();
        
        yield data.items;
        
        hasMore = data.hasNext;
        page++;
    }
}

// Utilisation
for await (const items of fetchPages('/api/users')) {
    console.log('Page items:', items);
}

// Timeout avec Promise.race
async function fetchWithTimeout(url, timeout = 5000) {
    const timeoutPromise = new Promise((_, reject) => {
        setTimeout(() => reject(new Error('Timeout')), timeout);
    });
    
    return Promise.race([
        fetch(url),
        timeoutPromise
    ]);
}

// Queue asynchrone
class AsyncQueue {
    constructor(concurrency = 1) {
        this.concurrency = concurrency;
        this.running = 0;
        this.queue = [];
    }
    
    async add(asyncFn) {
        return new Promise((resolve, reject) => {
            this.queue.push({
                asyncFn,
                resolve,
                reject
            });
            this.process();
        });
    }
    
    async process() {
        if (this.running >= this.concurrency || this.queue.length === 0) {
            return;
        }
        
        this.running++;
        const { asyncFn, resolve, reject } = this.queue.shift();
        
        try {
            const result = await asyncFn();
            resolve(result);
        } catch (error) {
            reject(error);
        } finally {
            this.running--;
            this.process();
        }
    }
}

🌐 API REST Mastery

Maîtrisez les API REST : Fetch avancé, authentification, gestion d'état et architecture.

🚀 Fetch API Avancé

Maîtrise complète de l'API Fetch moderne.

// Configuration avancée
const apiConfig = {
    baseURL: 'https://api.example.com',
    timeout: 10000,
    headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
    }
};

// Classe API wrapper
class ApiClient {
    constructor(config) {
        this.config = config;
        this.token = localStorage.getItem('authToken');
    }
    
    async request(endpoint, options = {}) {
        const url = `${this.config.baseURL}${endpoint}`;
        const config = {
            ...options,
            headers: {
                ...this.config.headers,
                ...options.headers,
                ...(this.token && { 
                    Authorization: `Bearer ${this.token}` 
                })
            }
        };
        
        const response = await fetch(url, config);
        
        if (!response.ok) {
            throw new ApiError(response.status, response.statusText);
        }
        
        return response.json();
    }
    
    // Méthodes CRUD
    get(endpoint) {
        return this.request(endpoint);
    }
    
    post(endpoint, data) {
        return this.request(endpoint, {
            method: 'POST',
            body: JSON.stringify(data)
        });
    }
    
    put(endpoint, data) {
        return this.request(endpoint, {
            method: 'PUT',
            body: JSON.stringify(data)
        });
    }
    
    delete(endpoint) {
        return this.request(endpoint, {
            method: 'DELETE'
        });
    }
}

🔐 Authentification

Gestion sécurisée de l'authentification et des tokens.

class AuthManager {
    constructor() {
        this.token = localStorage.getItem('authToken');
        this.refreshToken = localStorage.getItem('refreshToken');
    }
    
    async login(credentials) {
        try {
            const response = await fetch('/api/auth/login', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(credentials)
            });
            
            if (!response.ok) {
                throw new Error('Login failed');
            }
            
            const { token, refreshToken, user } = await response.json();
            
            this.setTokens(token, refreshToken);
            return { user, token };
        } catch (error) {
            console.error('Login error:', error);
            throw error;
        }
    }
    
    setTokens(token, refreshToken) {
        this.token = token;
        this.refreshToken = refreshToken;
        localStorage.setItem('authToken', token);
        localStorage.setItem('refreshToken', refreshToken);
    }
    
    async refreshAuthToken() {
        if (!this.refreshToken) {
            throw new Error('No refresh token available');
        }
        
        try {
            const response = await fetch('/api/auth/refresh', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ 
                    refreshToken: this.refreshToken 
                })
            });
            
            const { token } = await response.json();
            this.setTokens(token, this.refreshToken);
            return token;
        } catch (error) {
            this.logout();
            throw error;
        }
    }
    
    logout() {
        this.token = null;
        this.refreshToken = null;
        localStorage.removeItem('authToken');
        localStorage.removeItem('refreshToken');
    }
    
    isAuthenticated() {
        return !!this.token && !this.isTokenExpired();
    }
    
    isTokenExpired() {
        if (!this.token) return true;
        
        try {
            const payload = JSON.parse(atob(this.token.split('.')[1]));
            return payload.exp * 1000 < Date.now();
        } catch {
            return true;
        }
    }
}

💾 Cache & État

Gestion intelligente du cache et de l'état des données.

class DataManager {
    constructor() {
        this.cache = new Map();
        this.loading = new Set();
        this.subscribers = new Map();
    }
    
    async getData(key, fetcher, ttl = 300000) {
        // Si en cours de chargement, attendre
        if (this.loading.has(key)) {
            return this.waitForData(key);
        }
        
        // Vérifier le cache
        const cached = this.cache.get(key);
        if (cached && Date.now() - cached.timestamp < ttl) {
            return cached.data;
        }
        
        // Charger les données
        this.loading.add(key);
        
        try {
            const data = await fetcher();
            this.cache.set(key, {
                data,
                timestamp: Date.now()
            });
            
            this.notifySubscribers(key, data);
            return data;
        } finally {
            this.loading.delete(key);
        }
    }
    
    subscribe(key, callback) {
        if (!this.subscribers.has(key)) {
            this.subscribers.set(key, new Set());
        }
        this.subscribers.get(key).add(callback);
        
        return () => {
            const subs = this.subscribers.get(key);
            if (subs) {
                subs.delete(callback);
            }
        };
    }
    
    notifySubscribers(key, data) {
        const subscribers = this.subscribers.get(key);
        if (subscribers) {
            subscribers.forEach(callback => callback(data));
        }
    }
    
    invalidateCache(pattern) {
        if (typeof pattern === 'string') {
            this.cache.delete(pattern);
        } else if (pattern instanceof RegExp) {
            for (const [key] of this.cache) {
                if (pattern.test(key)) {
                    this.cache.delete(key);
                }
            }
        }
    }
}

🔄 Real-time

WebSockets et Server-Sent Events pour données temps réel.

class RealtimeManager {
    constructor(url) {
        this.url = url;
        this.ws = null;
        this.listeners = new Map();
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 5;
    }
    
    connect() {
        this.ws = new WebSocket(this.url);
        
        this.ws.onopen = () => {
            console.log('WebSocket connected');
            this.reconnectAttempts = 0;
        };
        
        this.ws.onmessage = (event) => {
            try {
                const data = JSON.parse(event.data);
                this.handleMessage(data);
            } catch (error) {
                console.error('Invalid message format:', error);
            }
        };
        
        this.ws.onclose = () => {
            console.log('WebSocket disconnected');
            this.attemptReconnect();
        };
        
        this.ws.onerror = (error) => {
            console.error('WebSocket error:', error);
        };
    }
    
    subscribe(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, new Set());
        }
        this.listeners.get(event).add(callback);
        
        return () => {
            const eventListeners = this.listeners.get(event);
            if (eventListeners) {
                eventListeners.delete(callback);
            }
        };
    }
    
    emit(event, data) {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify({ event, data }));
        }
    }
    
    handleMessage({ event, data }) {
        const listeners = this.listeners.get(event);
        if (listeners) {
            listeners.forEach(callback => callback(data));
        }
    }
    
    attemptReconnect() {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            const delay = Math.pow(2, this.reconnectAttempts) * 1000;
            
            setTimeout(() => {
                console.log(`Reconnecting... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
                this.connect();
            }, delay);
        }
    }
    
    disconnect() {
        if (this.ws) {
            this.ws.close();
        }
    }
}

📦 Modules ES6+

Architecture modulaire moderne : Import/Export, bundling, et organisation du code.

📤 Export Patterns

Différentes façons d'exporter des fonctionnalités.

// utils.js - Named exports
export function formatDate(date) {
    return date.toLocaleDateString('fr-FR');
}

export const API_BASE_URL = 'https://api.example.com';

export class Logger {
    static log(message) {
        console.log(`[${new Date().toISOString()}] ${message}`);
    }
}

// Export en fin de fichier
function calculateTotal(items) {
    return items.reduce((sum, item) => sum + item.price, 0);
}

function applyDiscount(total, discount) {
    return total * (1 - discount);
}

export { calculateTotal, applyDiscount };

// user.js - Default export
class User {
    constructor(name, email) {
        this.name = name;
        this.email = email;
    }
    
    getDisplayName() {
        return `${this.name} (${this.email})`;
    }
}

export default User;

// Mixed exports
export { User as DefaultUser };
export const USER_ROLES = ['admin', 'user', 'guest'];

📥 Import Patterns

Techniques d'importation flexibles et optimisées.

// Import nommés
import { formatDate, Logger, API_BASE_URL } from './utils.js';

// Import avec renommage
import { calculateTotal as getTotal, applyDiscount } from './utils.js';

// Import default
import User from './user.js';

// Import mixte
import User, { USER_ROLES } from './user.js';

// Import tout
import * as Utils from './utils.js';
Utils.formatDate(new Date());

// Import dynamique
async function loadModule() {
    const { default: User } = await import('./user.js');
    return new User('John', 'john@example.com');
}

// Import conditionnel
if (process.env.NODE_ENV === 'development') {
    const devTools = await import('./dev-tools.js');
    devTools.enableDebugMode();
}

// Re-export
// index.js
export { formatDate, Logger } from './utils.js';
export { default as User } from './user.js';
export * from './constants.js';

// Tree shaking friendly
import { debounce } from 'lodash-es'; // Seul debounce sera inclus

🏗️ Architecture

Organisation et architecture des modules d'une application.

// Structure recommandée
src/
├── components/
│   ├── ui/
│   │   ├── Button.js
│   │   ├── Modal.js
│   │   └── index.js
│   ├── forms/
│   │   ├── LoginForm.js
│   │   └── ContactForm.js
│   └── index.js
├── services/
│   ├── api/
│   │   ├── auth.js
│   │   ├── users.js
│   │   └── index.js
│   ├── storage.js
│   └── index.js
├── utils/
│   ├── date.js
│   ├── validation.js
│   └── index.js
├── constants/
│   ├── api.js
│   ├── app.js
│   └── index.js
└── main.js

// components/index.js - Barrel export
export { Button } from './ui/Button.js';
export { Modal } from './ui/Modal.js';
export { LoginForm } from './forms/LoginForm.js';
export { ContactForm } from './forms/ContactForm.js';

// services/index.js
export * as auth from './api/auth.js';
export * as users from './api/users.js';
export { StorageManager } from './storage.js';

// main.js - Point d'entrée
import { Button, Modal } from './components/index.js';
import { auth, StorageManager } from './services/index.js';
import { formatDate } from './utils/index.js';

⚙️ Bundling Avancé

Techniques avancées pour l'optimisation et le bundling.

// Dynamic imports pour code splitting
class ComponentLoader {
    static async loadComponent(name) {
        const components = {
            'user-profile': () => import('./components/UserProfile.js'),
            'admin-panel': () => import('./components/AdminPanel.js'),
            'dashboard': () => import('./components/Dashboard.js')
        };
        
        const loader = components[name];
        if (!loader) {
            throw new Error(`Component ${name} not found`);
        }
        
        const module = await loader();
        return module.default;
    }
}

// Lazy loading avec cache
const componentCache = new Map();

async function lazyLoad(componentName) {
    if (componentCache.has(componentName)) {
        return componentCache.get(componentName);
    }
    
    const component = await ComponentLoader.loadComponent(componentName);
    componentCache.set(componentName, component);
    return component;
}

// Web Workers avec modules
// worker.js
import { processData } from './utils/data-processor.js';

self.onmessage = async function(e) {
    const { data, options } = e.data;
    const result = await processData(data, options);
    self.postMessage(result);
};

// main.js
const worker = new Worker('./worker.js', { type: 'module' });
worker.postMessage({ data: largeDataset, options: {} });

// Service Worker avec modules
// sw.js
import { CacheManager } from './cache-manager.js';

const cacheManager = new CacheManager();

self.addEventListener('fetch', async (event) => {
    event.respondWith(cacheManager.handleRequest(event.request));
});

🧠 Techniques d'Expert

Patterns avancés, optimisations performance et techniques d'expert pour applications complexes.

🎨 Design Patterns

Patterns essentiels pour architectures robustes.

// Singleton Pattern
class AppConfig {
    constructor() {
        if (AppConfig.instance) {
            return AppConfig.instance;
        }
        
        this.settings = new Map();
        AppConfig.instance = this;
    }
    
    static getInstance() {
        return new AppConfig();
    }
}

// Observer Pattern
class EventEmitter {
    constructor() {
        this.events = new Map();
    }
    
    on(event, callback) {
        if (!this.events.has(event)) {
            this.events.set(event, new Set());
        }
        this.events.get(event).add(callback);
        
        return () => this.off(event, callback);
    }
    
    emit(event, data) {
        const callbacks = this.events.get(event);
        if (callbacks) {
            callbacks.forEach(callback => callback(data));
        }
    }
    
    off(event, callback) {
        const callbacks = this.events.get(event);
        if (callbacks) {
            callbacks.delete(callback);
        }
    }
}

// Factory Pattern
class ComponentFactory {
    static create(type, props) {
        const components = {
            'button': () => new Button(props),
            'input': () => new Input(props),
            'modal': () => new Modal(props)
        };
        
        const creator = components[type];
        if (!creator) {
            throw new Error(`Unknown component type: ${type}`);
        }
        
        return creator();
    }
}

⚡ Optimisations

Techniques de performance et optimisation mémoire.

// Memoization
function memoize(fn) {
    const cache = new Map();
    
    return function(...args) {
        const key = JSON.stringify(args);
        
        if (cache.has(key)) {
            return cache.get(key);
        }
        
        const result = fn.apply(this, args);
        cache.set(key, result);
        return result;
    };
}

const expensiveCalculation = memoize((n) => {
    // Calcul coûteux
    return n * n * n;
});

// Debounce & Throttle
function debounce(fn, delay) {
    let timeoutId;
    
    return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => fn.apply(this, args), delay);
    };
}

function throttle(fn, limit) {
    let inThrottle;
    
    return function(...args) {
        if (!inThrottle) {
            fn.apply(this, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
}

// WeakMap pour éviter fuites mémoire
const privateData = new WeakMap();

class User {
    constructor(name, email) {
        privateData.set(this, {
            name,
            email,
            loginCount: 0
        });
    }
    
    getName() {
        return privateData.get(this).name;
    }
    
    incrementLogin() {
        const data = privateData.get(this);
        data.loginCount++;
    }
}

🔧 Programmation Fonctionnelle

Approches fonctionnelles modernes en JavaScript.

// Curry & Partial Application
const curry = (fn) => {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        }
        
        return function(...nextArgs) {
            return curried.apply(this, [...args, ...nextArgs]);
        };
    };
};

const add = curry((a, b, c) => a + b + c);
const addTen = add(10);
const addTenAndFive = addTen(5);
console.log(addTenAndFive(3)); // 18

// Composition de fonctions
const compose = (...fns) => (value) => 
    fns.reduceRight((acc, fn) => fn(acc), value);

const pipe = (...fns) => (value) => 
    fns.reduce((acc, fn) => fn(acc), value);

// Example d'utilisation
const users = [
    { name: 'Alice', age: 25, active: true },
    { name: 'Bob', age: 30, active: false },
    { name: 'Carol', age: 35, active: true }
];

const processUsers = pipe(
    users => users.filter(user => user.active),
    users => users.map(user => ({ ...user, ageGroup: user.age < 30 ? 'young' : 'adult' })),
    users => users.sort((a, b) => a.name.localeCompare(b.name))
);

const result = processUsers(users);

// Immutable updates
const updateUser = (user, updates) => ({
    ...user,
    ...updates,
    updatedAt: new Date()
});

// Lens pattern pour deep updates
const lens = (path) => {
    const getter = (obj) => path.reduce((current, key) => current?.[key], obj);
    const setter = (obj, value) => {
        const clone = structuredClone(obj);
        let current = clone;
        
        for (let i = 0; i < path.length - 1; i++) {
            current = current[path[i]];
        }
        
        current[path[path.length - 1]] = value;
        return clone;
    };
    
    return { get: getter, set: setter };
};

🎯 Proxy & Reflection

Meta-programmation avancée avec Proxy et Reflect.

// Validation avec Proxy
function createValidatedObject(schema) {
    return new Proxy({}, {
        set(target, property, value) {
            const validator = schema[property];
            
            if (validator && !validator(value)) {
                throw new Error(`Invalid value for ${property}: ${value}`);
            }
            
            return Reflect.set(target, property, value);
        },
        
        get(target, property) {
            if (property in target) {
                return Reflect.get(target, property);
            }
            
            throw new Error(`Property ${property} does not exist`);
        }
    });
}

// Utilisation
const userSchema = {
    name: value => typeof value === 'string' && value.length > 0,
    age: value => typeof value === 'number' && value >= 0,
    email: value => typeof value === 'string' && value.includes('@')
};

const user = createValidatedObject(userSchema);

// Observable avec Proxy
function createObservable(target, onChange) {
    return new Proxy(target, {
        set(obj, property, value) {
            const oldValue = obj[property];
            const result = Reflect.set(obj, property, value);
            
            if (oldValue !== value) {
                onChange(property, value, oldValue);
            }
            
            return result;
        }
    });
}

const state = createObservable({}, (property, newValue, oldValue) => {
    console.log(`${property} changed from ${oldValue} to ${newValue}`);
});

// API fluide avec Proxy
function createFluentAPI() {
    const operations = [];
    
    return new Proxy({}, {
        get(target, property) {
            if (property === 'execute') {
                return () => operations.reduce((result, op) => op(result), null);
            }
            
            return (...args) => {
                operations.push(data => ({
                    ...data,
                    [property]: args.length === 1 ? args[0] : args
                }));
                return target;
            };
        }
    });
}

// Utilisation
const query = createFluentAPI();
const result = query
    .select('name', 'email')
    .where('age', '>', 18)
    .limit(10)
    .execute();

🏋️ Exercices Pratiques

Mettez en pratique vos connaissances avec ces exercices progressifs de complexité croissante.

🎯 Exercice 1 : API Manager Avancé

Difficile

📋 Objectifs :

  • • Créer une classe ApiManager complète
  • • Intégrer authentification JWT avec refresh automatique
  • • Implémenter cache intelligent avec TTL
  • • Gestion d'erreurs robuste avec retry automatique
  • • Support des requêtes parallèles optimisées

🔧 Fonctionnalités requises :

  • CRUD complet : GET, POST, PUT, DELETE
  • Interceptors : Request/Response
  • Cache : TTL, invalidation, stratégies
  • Auth : Login, refresh, logout automatique
  • Retry : Backoff exponentiel
  • Events : Loading, success, error

💡 Structure de base :

class ApiManager {
    constructor(config) {
        // Configuration : baseURL, timeout, headers
        // Cache : Map avec TTL
        // Auth : tokens, refresh logic
        // Events : EventEmitter
    }
    
    // Authentification
    async login(credentials) { /* ... */ }
    async refreshToken() { /* ... */ }
    logout() { /* ... */ }
    
    // Requêtes CRUD
    async get(endpoint, options) { /* ... */ }
    async post(endpoint, data, options) { /* ... */ }
    async put(endpoint, data, options) { /* ... */ }
    async delete(endpoint, options) { /* ... */ }
    
    // Gestion cache
    getCached(key) { /* ... */ }
    setCache(key, data, ttl) { /* ... */ }
    invalidateCache(pattern) { /* ... */ }
    
    // Utilitaires
    async request(config) { /* ... */ }
    addInterceptor(type, handler) { /* ... */ }
}

💡 Conseil : Commencez par la structure de base, puis ajoutez progressivement chaque fonctionnalité. Testez chaque partie avant de passer à la suivante.

🚀 Exercice 2 : State Manager Réactif

Expert

📋 Objectifs :

  • • Créer un gestionnaire d'état réactif avec Proxy
  • • Implémenter actions, mutations et modules
  • • Middleware système (logging, persistence, devtools)
  • • Time-travel debugging
  • • Performance optimizations

⚙️ Architecture :

  • Store : État global avec modules
  • Actions : Dispatch asynchrone
  • Mutations : Modifications synchrones
  • Getters : Computed properties
  • Plugins : Système extensible
  • DevTools : Debug interface

🎨 Exemple d'utilisation :

const store = new StateManager({
    modules: {
        user: {
            state: { profile: null, loading: false },
            mutations: {
                SET_LOADING(state, loading) {
                    state.loading = loading;
                },
                SET_PROFILE(state, profile) {
                    state.profile = profile;
                }
            },
            actions: {
                async fetchProfile({ commit }, userId) {
                    commit('SET_LOADING', true);
                    try {
                        const profile = await api.get(`/users/${userId}`);
                        commit('SET_PROFILE', profile);
                    } finally {
                        commit('SET_LOADING', false);
                    }
                }
            },
            getters: {
                isLoggedIn: state => !!state.profile,
                userName: state => state.profile?.name || 'Guest'
            }
        }
    },
    plugins: [persistencePlugin, loggingPlugin]
});

// Utilisation
store.dispatch('user/fetchProfile', 123);
store.subscribe(state => updateUI(state));

🌐 Exercice 3 : Micro-Frontend Loader

Architecture

📋 Mission :

  • • Système de chargement dynamique de micro-frontends
  • • Isolation des modules (sandbox)
  • • Communication inter-modules
  • • Gestion des dépendances partagées
  • • Hot-reload en développement

🔧 Défis techniques :

  • Module Federation : Chargement dynamique
  • Sandboxing : Isolation CSS/JS
  • Lifecycle : Mount/unmount
  • Routing : Navigation distribuée
  • State : Partage de données

🏗️ Architecture cible :

const microFrontendLoader = new MicroFrontendLoader({
    registry: 'https://registry.example.com',
    container: '#app-container',
    sandbox: true
});

// Configuration des micro-frontends
await microFrontendLoader.register([
    {
        name: 'header',
        url: 'https://header.example.com/bundle.js',
        mount: '#header',
        props: { user: currentUser }
    },
    {
        name: 'dashboard',
        url: 'https://dashboard.example.com/bundle.js',
        mount: '#main-content',
        route: '/dashboard/*'
    }
]);

// Communication entre modules
microFrontendLoader.on('user-updated', (user) => {
    microFrontendLoader.broadcast('user-changed', user);
});

// Chargement conditionnel
if (userRole === 'admin') {
    await microFrontendLoader.load('admin-panel');
}

🎯 Challenge : Cet exercice vous prépare aux architectures modernes de grandes applications. Concentrez-vous sur l'isolation et la communication entre modules.