Modèles JavaScript Modernes
Guide complet pour maîtriser les fonctionnalités JavaScript modernes (ES6+), les modèles de programmation fonctionnelle, et les bonnes pratiques pour écrire du code propre, maintenable et performant.
Quand Utiliser Cette Compétence
- Refactoriser du JavaScript legacy vers la syntaxe moderne
- Implémenter des modèles de programmation fonctionnelle
- Optimiser les performances JavaScript
- Écrire du code maintenable et lisible
- Travailler avec les opérations asynchrones
- Construire des applications web modernes
- Migrer des callbacks vers les Promises/async-await
- Implémenter des pipelines de transformation de données
Fonctionnalités Essentielles ES6+
1. Fonctions Fléchées
Syntaxe et Cas d'Usage :
// Fonction traditionnelle
function add(a, b) {
return a + b;
}
// Fonction fléchée
const add = (a, b) => a + b;
// Paramètre unique (parenthèses optionnelles)
const double = (x) => x * 2;
// Aucun paramètre
const getRandom = () => Math.random();
// Plusieurs instructions (nécessite des accolades)
const processUser = (user) => {
const normalized = user.name.toLowerCase();
return { ...user, name: normalized };
};
// Retourner des objets (entourer de parenthèses)
const createUser = (name, age) => ({ name, age });
Liaison Lexicale de 'this' :
class Counter {
constructor() {
this.count = 0;
}
// La fonction fléchée préserve le contexte 'this'
increment = () => {
this.count++;
};
// La fonction traditionnelle perd 'this' dans les callbacks
incrementTraditional() {
setTimeout(function () {
this.count++; // 'this' est undefined
}, 1000);
}
// La fonction fléchée maintient 'this'
incrementArrow() {
setTimeout(() => {
this.count++; // 'this' fait référence à l'instance Counter
}, 1000);
}
}
2. Destructuration
Destructuration d'Objets :
const user = {
id: 1,
name: "John Doe",
email: "john@example.com",
address: {
city: "New York",
country: "USA",
},
};
// Destructuration basique
const { name, email } = user;
// Renommer les variables
const { name: userName, email: userEmail } = user;
// Valeurs par défaut
const { age = 25 } = user;
// Destructuration imbriquée
const {
address: { city, country },
} = user;
// Opérateur rest
const { id, ...userWithoutId } = user;
// Paramètres de fonction
function greet({ name, age = 18 }) {
console.log(`Hello ${name}, you are ${age}`);
}
greet(user);
Destructuration de Tableaux :
const numbers = [1, 2, 3, 4, 5];
// Destructuration basique
const [first, second] = numbers;
// Ignorer les éléments
const [, , third] = numbers;
// Opérateur rest
const [head, ...tail] = numbers;
// Échange de variables
let a = 1,
b = 2;
[a, b] = [b, a];
// Valeurs de retour de fonction
function getCoordinates() {
return [10, 20];
}
const [x, y] = getCoordinates();
// Valeurs par défaut
const [one, two, three = 0] = [1, 2];
3. Opérateurs Spread et Rest
Opérateur Spread :
// Spread de tableaux
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
// Spread d'objets
const defaults = { theme: "dark", lang: "en" };
const userPrefs = { theme: "light" };
const settings = { ...defaults, ...userPrefs };
// Arguments de fonction
const numbers = [1, 2, 3];
Math.max(...numbers);
// Copier des tableaux/objets (copie superficielle)
const copy = [...arr1];
const objCopy = { ...user };
// Ajouter des éléments de manière immuable
const newArr = [...arr1, 4, 5];
const newObj = { ...user, age: 30 };
Paramètres Rest :
// Collecter les arguments de fonction
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
sum(1, 2, 3, 4, 5);
// Avec des paramètres réguliers
function greet(greeting, ...names) {
return `${greeting} ${names.join(", ")}`;
}
greet("Hello", "John", "Jane", "Bob");
// Rest d'objet
const { id, ...userData } = user;
// Rest de tableau
const [first, ...rest] = [1, 2, 3, 4, 5];
4. Modèles de Chaînes de Caractères
// Utilisation basique
const name = "John";
const greeting = `Hello, ${name}!`;
// Chaînes multi-lignes
const html = `
<div>
<h1>${title}</h1>
<p>${content}</p>
</div>
`;
// Évaluation d'expression
const price = 19.99;
const total = `Total: $${(price * 1.2).toFixed(2)}`;
// Modèles de chaînes étiquetés
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] || "";
return result + str + `<mark>${value}</mark>`;
}, "");
}
const name = "John";
const age = 30;
const html = highlight`Name: ${name}, Age: ${age}`;
// Sortie : "Name: <mark>John</mark>, Age: <mark>30</mark>"
5. Littéraux d'Objets Améliorés
const name = "John";
const age = 30;
// Noms de propriétés raccourcis
const user = { name, age };
// Noms de méthodes raccourcis
const calculator = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
},
};
// Noms de propriétés calculés
const field = "email";
const user = {
name: "John",
[field]: "john@example.com",
[`get${field.charAt(0).toUpperCase()}${field.slice(1)}`]() {
return this[field];
},
};
// Création dynamique de propriétés
const createUser = (name, ...props) => {
return props.reduce(
(user, [key, value]) => ({
...user,
[key]: value,
}),
{ name },
);
};
const user = createUser("John", ["age", 30], ["email", "john@example.com"]);
Modèles Asynchrones
1. Promises
Créer et Utiliser des Promises :
// Créer une promise
const fetchUser = (id) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: "John" });
} else {
reject(new Error("Invalid ID"));
}
}, 1000);
});
};
// Utiliser les promises
fetchUser(1)
.then((user) => console.log(user))
.catch((error) => console.error(error))
.finally(() => console.log("Done"));
// Chaîner les promises
fetchUser(1)
.then((user) => fetchUserPosts(user.id))
.then((posts) => processPosts(posts))
.then((result) => console.log(result))
.catch((error) => console.error(error));
Combinateurs de Promises :
// Promise.all - Attendre toutes les promises
const promises = [fetchUser(1), fetchUser(2), fetchUser(3)];
Promise.all(promises)
.then((users) => console.log(users))
.catch((error) => console.error("At least one failed:", error));
// Promise.allSettled - Attendre toutes, quel que soit le résultat
Promise.allSettled(promises).then((results) => {
results.forEach((result) => {
if (result.status === "fulfilled") {
console.log("Success:", result.value);
} else {
console.log("Error:", result.reason);
}
});
});
// Promise.race - La première à se terminer
Promise.race(promises)
.then((winner) => console.log("First:", winner))
.catch((error) => console.error(error));
// Promise.any - La première à réussir
Promise.any(promises)
.then((first) => console.log("First success:", first))
.catch((error) => console.error("All failed:", error));
2. Async/Await
Utilisation Basique :
// Une fonction async retourne toujours une Promise
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}
// Gestion des erreurs avec try/catch
async function getUserData(id) {
try {
const user = await fetchUser(id);
const posts = await fetchUserPosts(user.id);
return { user, posts };
} catch (error) {
console.error("Error fetching data:", error);
throw error;
}
}
// Exécution séquentielle vs parallèle
async function sequential() {
const user1 = await fetchUser(1); // Attendre
const user2 = await fetchUser(2); // Puis attendre
return [user1, user2];
}
async function parallel() {
const [user1, user2] = await Promise.all([fetchUser(1), fetchUser(2)]);
return [user1, user2];
}
Modèles Avancés :
// Async IIFE
(async () => {
const result = await someAsyncOperation();
console.log(result);
})();
// Itération async
async function processUsers(userIds) {
for (const id of userIds) {
const user = await fetchUser(id);
await processUser(user);
}
}
// Await au niveau supérieur (ES2022)
const config = await fetch("/config.json").then((r) => r.json());
// Logique de tentative
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fetch(url);
} catch (error) {
if (i === retries - 1) throw error;
await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
// Wrapper avec délai d'attente
async function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), ms),
);
return Promise.race([promise, timeout]);
}
Modèles de Programmation Fonctionnelle
La programmation fonctionnelle en JavaScript repose sur les fonctions pures, l'immuabilité et les transformations composables.
Les sujets clés couverts dans references/advanced-patterns.md :
- Méthodes de tableau —
map,filter,reduce,find,findIndex,some,every,flatMap,Array.from - Fonctions d'ordre supérieur —
forEach/map/filterpersonnalisés, curryfication, application partielle, mémoïsation - Composition et piping — utilitaires
compose/pipeavec des exemples pratiques de transformation de données - Fonctions pures et immuabilité — opérations immuables sur tableaux/objets, clonage profond avec
structuredClone
Fonctionnalités Modernes des Classes
Les classes ES2022 prennent en charge les champs privés (#field), les champs statiques, les getters/setters et les méthodes privées. Voir references/advanced-patterns.md pour un exemple complet avec héritage.
Modules (ES6)
// Exports nommés
export const PI = 3.14159;
export function add(a, b) { return a + b; }
// Export par défaut
export default function multiply(a, b) { return a * b; }
// Import
import multiply, { PI, add } from "./math.js";
// Import dynamique (séparation de code)
const { add } = await import("./math.js");
Pour les ré-exports, les imports d'espace de noms et le chargement dynamique conditionnel, voir references/advanced-patterns.md.
Itérateurs et Générateurs
Les générateurs (function*) et les générateurs async (async function*) permettent des séquences paresseuses et la pagination async. Voir references/advanced-patterns.md pour des exemples d'itérateur personnalisé, générateur de plage, fibonacci et for await...of.
Opérateurs Modernes
// Chaînage optionnel — accès sécurisé aux propriétés
const city = user?.address?.city;
const result = obj.method?.();
// Fusion nulle — valeur par défaut uniquement pour null/undefined (pas 0 ou "")
const value = null ?? "default"; // 'default'
const zero = 0 ?? "default"; // 0
// Assignment logique
a ??= "default"; // assigner si null/undefined
obj.count ||= 1; // assigner si falsy
obj.count &&= 2; // assigner si truthy
Optimisation des Performances
Voir references/advanced-patterns.md pour debounce, throttle et évaluation paresseuse avec générateurs.
Bonnes Pratiques
- Utiliser const par défaut : N'utiliser let que si la réaffectation est nécessaire
- Préférer les fonctions fléchées : Particulièrement pour les callbacks
- Utiliser les modèles de chaînes : Au lieu de la concaténation de chaînes
- Destructurer les objets et les tableaux : Pour un code plus propre
- Utiliser async/await : Au lieu des chaînes de promises
- Éviter de muter les données : Utiliser l'opérateur spread et les méthodes de tableau
- Utiliser le chaînage optionnel : Prévenir « Cannot read property of undefined »
- Utiliser la fusion nulle : Pour les valeurs par défaut
- Préférer les méthodes de tableau : Aux boucles traditionnelles
- Utiliser les modules : Pour une meilleure organisation du code
- Écrire des fonctions pures : Plus faciles à tester et à raisonner
- Utiliser des noms de variables significatifs : Code autodocumenté
- Garder les fonctions petites : Principe de responsabilité unique
- Gérer les erreurs correctement : Utiliser try/catch avec async/await
- Utiliser le mode strict :
'use strict'pour une meilleure détection d'erreurs
Pour les pièges courants (liaison this, anti-patterns promise, fuites mémoire), voir references/advanced-patterns.md.