JavaScript avancé
ES6+, asynchrone, API REST et techniques d’expert : le JavaScript des applications complexes.
- ES6+
- Async/await
- API REST
- Techniques d’expert
⚡ 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.
javascript
// 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.
javascript
// 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.
javascript
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 = `
<div class="user-card">
<h2>${name}</h2>
<p>Age: ${age}</p>
</div>
`;
// Tagged template literals
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
const value = values[i] ? `<mark>${values[i]}</mark>` : '';
return result + string + value;
}, '');
}
const highlighted = highlight`Hello ${name}, welcome!`;
🎯 Spread & Rest
Opérateurs polyvalents pour manipulation de données.
javascript
// 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.
javascript
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.
javascript
// 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.
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.
javascript
// 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.
javascript
// 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.
javascript
// 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.
javascript
// 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.
javascript
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.
javascript
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.
javascript
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.
javascript
// 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.
javascript
// 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.
javascript
// 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.
javascript
// 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.
javascript
// 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.
javascript
// 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.
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.
javascript
// 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 :
javascript
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 :
javascript
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 :
javascript
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.
JavaScript avancé, acquis
ES6+, asynchrone, modules et techniques d’expert : vous avez de quoi bâtir des applications complètes.