Programmation Web

JavaScript
Les Bases

Variables, types, fonctions, tableaux, objets, asynchrone et DOM — les fondations solides du JavaScript moderne.

JS — c'est quoi ?

ℹ️

JavaScript est un langage interprété, dynamiquement typé et orienté événements. Il tourne dans le navigateur (côté client) et côté serveur (Node.js). C'est l'unique langage natif du web — HTML structure, CSS stylise, JS fait agir.

Les 3 façons d'inclure JS
<!-- 1. Inline (à éviter sauf cas spéciaux) -->
<button onclick="alert('Clic!')">Clic</button>

<!-- 2. Balise <script> dans la page -->
<script>
  console.log('Bonjour');
</script>

<!-- 3. Fichier externe (recommandé) -->
<!-- defer : exécute après le parsing du HTML -->
<script src="app.js" defer></script>

<!-- async : exécute dès le téléchargement -->
<script src="analytics.js" async></script>
Outils de debug indispensables
// Afficher dans la console du navigateur (F12)
console.log('message simple');
console.log('valeur :', maVariable);
console.warn('avertissement');
console.error('erreur');
console.table([{ nom: 'Alice', age: 25 }]);
console.group('Groupe'); console.groupEnd();
console.time('timer'); console.timeEnd('timer');

// Inspecter un objet
console.dir(document.body);

// Débogage — point d'arrêt dans le code
debugger; // pause l'exécution si DevTools ouvert

// 'use strict' — active le mode strict
// Détecte des erreurs silencieuses, interdit
// les variables non déclarées
'use strict';

// typeof — connaître le type d'une valeur
typeof "bonjour"   // "string"
typeof 42          // "number"
typeof true        // "boolean"
typeof undefined   // "undefined"
typeof null        // "object" ← bug historique !
typeof []          // "object" ← utiliser Array.isArray()
typeof function(){} // "function"

Variables — var, let, const

varletconst
PortéeFonctionBloc { }Bloc { }
Réassignable
Redéclarable
HoistingOui (undefined)Oui (TDZ*)Oui (TDZ*)
Global windowOuiNonNon
Utiliser ?❌ Jamais✅ Si valeur change✅ Par défaut
💡

*TDZ (Temporal Dead Zone) : let et const sont hoistés mais pas initialisés. Y accéder avant leur déclaration lève une ReferenceError — contrairement à var qui renvoie undefined silencieusement.

Exemples pratiques
// ✅ const par défaut — valeur ne change pas
const PI = 3.14159;
const URL_API = 'https://api.exemple.com';

// ✅ const pour les objets et tableaux
// (la référence est constante, pas le contenu !)
const user = { nom: 'Alice', age: 25 };
user.age = 26; // ✅ OK — modifie le contenu
// user = {} // ✗ Erreur — réassignation interdite

// ✅ let si la valeur change
let compteur = 0;
compteur++;  // OK
let message = 'Bonjour';
message = 'Salut'; // OK

// Problème classique de var en boucle :
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
} // Affiche 3, 3, 3 (pas 0, 1, 2 !)

for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 100);
} // Affiche 0, 1, 2 ✅

Types de données

TypeExemplesParticularités
string 'bonjour' "texte" `template` Immuable. Unicode complet. 3 formes de guillemets.
number 42 3.14 NaN Infinity Un seul type numérique (IEEE 754 double). NaN !== NaN !
boolean true false Résultat de comparaisons.
null null Absence intentionnelle de valeur. Typeof = "object" (bug).
undefined undefined Variable déclarée mais non initialisée.
Symbol Symbol('id') Clé unique et immuable. Utile pour éviter les collisions.
BigInt 9007199254740993n Entiers > Number.MAX_SAFE_INTEGER.
object {} [] new Date() Type référence — passé par référence, pas par valeur.
Pièges classiques
// NaN — Not a Number
typeof NaN              // "number" (😱)
NaN === NaN             // false !
Number.isNaN(NaN)       // true ✅
Number.isNaN('texte')  // false (≠ isNaN global)

// null vs undefined
null == undefined      // true  (égalité lâche)
null === undefined     // false (égalité stricte)

// Référence vs valeur
const a = { x: 1 };
const b = a;      // b pointe sur le même objet
b.x = 99;
console.log(a.x); // 99 ! (même référence)

// Cloner un objet (shallow copy)
const c = { ...a };     // spread
const d = Object.assign({}, a);
// Deep copy :
const e = structuredClone(a); // ES2022 ✅
const f = JSON.parse(JSON.stringify(a)); // vieux

// Conversion explicite
Number('42')      // 42
Number('')        // 0  ← surprenant
Number(null)     // 0
Number(undefined) // NaN
String(42)        // "42"
Boolean(0)        // false

Opérateurs

Comparaison — == vs ===
// == égalité lâche (avec coercition de type)
0  == ''       // true  ← dangereux
0  == '0'      // true
null == undefined // true
false == 0    // true

// === égalité stricte (sans coercition) ✅
0   === ''      // false
42  === 42      // true
'a' === 'a'    // true

// Toujours utiliser === sauf null check
if (valeur == null) { }  // catch null ET undefined

// Opérateurs logiques
&&   // ET — retourne la première valeur falsy
||   // OU — retourne la première valeur truthy
!    // NON
??   // Nullish coalescing — retourne droite si null/undefined

// Court-circuit
const nom = user && user.nom;    // si user est truthy
const val = input || 'défaut';   // valeur par défaut
const v   = data ?? 'défaut';   // null/undefined seulement
Opérateurs modernes
// Optional chaining ?. (ES2020)
// Évite "Cannot read property of undefined"
const ville = user?.adresse?.ville;
// Équivalent à :
// user && user.adresse && user.adresse.ville

user?.nom                // propriété optionnelle
tableau?.[0]            // index optionnel
fonc?.()                 // appel optionnel

// Nullish assignment ??= (ES2021)
user.prenom ??= 'Inconnu';
// Assigne seulement si null ou undefined

// Logical assignment ||= et &&=
config.debug ||= false;
// config.debug = config.debug || false

// Exponentiation **
2 ** 10   // 1024 (= Math.pow(2, 10))

// Ternaire
const statut = age >= 18 ? 'majeur' : 'mineur';

// Comma — évalue gauche et droite, retourne droite
let x = (1, 2, 3); // x = 3

Coercition & truthy / falsy

JavaScript convertit automatiquement les types dans certains contextes — c'est la coercition de type. Comprendre les valeurs truthy/falsy évite de nombreux bugs.

✗ Falsy — sont faux dans un if()
false
0   -0   0n (BigInt zéro)
""   ''   `` (chaîne vide)
null
undefined
NaN
✓ Truthy — tout le reste
"0"  ← chaîne non vide !
"false"  ← chaîne non vide !
[]  ← tableau vide !
{}  ← objet vide !
function() {}
42, -1, Infinity
Coercition — les surprises
// Addition + : coercition vers string
"3" + 4           // "34"  ← concaténation !
1 + 2 + "3"      // "33"  ← 1+2=3 puis "3"+"3"
"3" + 1 + 2      // "312" ← "3"+1="31" puis "31"+2

// Autres opérateurs : coercition vers number
"5" - 2           // 3    ← soustraction numérique
"5" * "2"         // 10
true + true      // 2    ← true = 1
[] + []           // ""   ← [].toString() = ""
[] + {}           // "[object Object]"

// Bonnes pratiques pour éviter les surprises :
const n = Number(input) || 0;  // conversion explicite
if (tableau.length === 0) {}  // pas juste if(!tableau)

Conditions

if / else if / else
const note = 14;

if (note >= 16) {
  console.log('Distinction');
} else if (note >= 10) {
  console.log('Réussi');
} else {
  console.log('Échec');
}

// switch — comparaison stricte (===)
switch (jour) {
  case 'lundi':
  case 'mardi':   // fall-through intentionnel
    console.log('Début de semaine');
    break;
  case 'vendredi':
    console.log('TGIF');
    break;
  default:
    console.log('Milieu de semaine');
}

// Ternaire imbriqué (à éviter si trop long)
const cat = note >= 16 ? 'A'
           : note >= 14 ? 'B'
           : note >= 10 ? 'C'
           : 'F';
Patterns modernes
// Guard clauses — sortir tôt plutôt qu'imbriquer
// ✗ À éviter :
function traiter(user) {
  if (user) {
    if (user.actif) {
      // traitement...
    }
  }
}

// ✅ Guard clauses :
function traiter(user) {
  if (!user) return;
  if (!user.actif) return;
  // traitement principal ici — peu imbriqué
}

// Object lookup — remplace un gros switch
const actions = {
  'ajouter':   () => panier.push(item),
  'supprimer': () => panier.pop(),
  'vider':     () => panier.splice(0),
};

const fn = actions[action];
if (fn) fn();  // appeler si l'action existe

Boucles

for, while, do...while
// for classique
for (let i = 0; i < 5; i++) {
  console.log(i);  // 0, 1, 2, 3, 4
}

// for...of — itérer sur les valeurs
const fruits = ['pomme', 'banane', 'cerise'];
for (const fruit of fruits) {
  console.log(fruit);  // pomme, banane, cerise
}
// Fonctionne aussi sur : string, Map, Set, NodeList

// for...in — itérer sur les clés d'un objet
const personne = { nom: 'Bob', age: 30 };
for (const cle in personne) {
  console.log(cle, personne[cle]);
}
// ⚠ Éviter for...in sur les tableaux

// while
let n = 1;
while (n < 100) { n *= 2; }

// break et continue
for (const x of nombres) {
  if (x < 0) continue;  // sauter les négatifs
  if (x > 100) break;   // arrêter si > 100
  traiter(x);
}
Méthodes de tableau — préférer à for
const notes = [12, 15, 8, 18, 10];

// forEach — pour les effets de bord
notes.forEach((n, i) => console.log(i, n));

// map — transformer chaque élément
const doubles = notes.map(n => n * 2);
// [24, 30, 16, 36, 20]

// filter — garder selon condition
const reçus = notes.filter(n => n >= 10);
// [12, 15, 18, 10]

// find — premier élément correspondant
const premiere = notes.find(n => n > 14);
// 15

// reduce — accumuler en une valeur
const somme = notes.reduce((acc, n) => acc + n, 0);
// 63

// some / every
notes.some(n => n >= 18)   // true (au moins un)
notes.every(n => n >= 10)  // false (pas tous)

// Chaîner les méthodes
const moyenneReçus = notes
  .filter(n => n >= 10)
  .reduce((a, n, _, arr) => a + n / arr.length, 0);

Fonctions

Les 4 façons de définir une fonction
// 1. Déclaration — hoistée, utilisable avant la def
function saluer(nom) {
  return `Bonjour, ${nom} !`;
}

// 2. Expression — non hoistée
const saluer = function(nom) {
  return `Bonjour, ${nom} !`;
};

// 3. Arrow function (ES6) — pas de this propre
const saluer = (nom) => `Bonjour, ${nom} !`;
const double = n => n * 2;       // 1 paramètre : pas de ()  
const objet  = () => ({ x: 1 }); // retourner un objet : ({})

// 4. Méthode dans un objet
const calc = {
  valeur: 0,
  ajouter(n) { this.valeur += n; },  // this = calc
  reset: () => { /* this ≠ calc ici ! */ },
};
Paramètres avancés
// Valeurs par défaut
function créer(nom, role = 'utilisateur', actif = true) {
  return { nom, role, actif };
}
créer('Alice'); // {nom:'Alice', role:'utilisateur', actif:true}

// Rest parameter — nombre variable d'arguments
function somme(...nombres) {
  return nombres.reduce((a, b) => a + b, 0);
}
somme(1, 2, 3, 4); // 10

// Destructuring en paramètres
function afficher({ nom, age, ville = 'Inconnue' }) {
  console.log(`${nom}, ${age} ans, ${ville}`);
}
afficher({ nom: 'Bob', age: 30 });

// Fonctions d'ordre supérieur
const multiplier = (x) => (y) => x * y;
const double = multiplier(2);
const triple = multiplier(3);
double(5); // 10
triple(5); // 15

// IIFE — Immediately Invoked Function Expression
(function() {
  const prive = 'ne pollue pas le scope global';
})();

Portée (scope) & closures

Global scope
const URL = 'https://...';
fonction scope
const local = 42;
bloc scope { }
let x = 1; ← let/const
// accès à local ✓
// accès à URL ✓
// x inaccessible ici ✗
Closures — capturer le scope parent
// Une closure = fonction qui "mémorise"
// les variables de son scope parent,
// même après que celui-ci ait terminé.

function créerCompteur() {
  let count = 0;  // variable privée !

  return {
    incrémenter() { count++; },
    décrémenter() { count--; },
    valeur()      { return count; },
  };
}

const c = créerCompteur();
c.incrémenter();
c.incrémenter();
c.valeur(); // 2
// count est inaccessible depuis l'extérieur !

// Utilisation pratique — mémorisation
function memoize(fn) {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

const fibMemo = memoize(function fib(n) {
  return n <= 1 ? n : fibMemo(n-1) + fibMemo(n-2);
});

Tableaux (Array)

Méthodes de mutation (modifient le tableau)
const arr = [1, 2, 3];

// Ajouter / supprimer
arr.push(4);         // fin   → [1,2,3,4]
arr.pop();           // fin   → [1,2,3]
arr.unshift(0);     // début → [0,1,2,3]
arr.shift();         // début → [1,2,3]

// splice — couteau suisse
arr.splice(1, 1);         // supprimer index 1 → [1,3]
arr.splice(1, 0, 99);    // insérer 99 à index 1
arr.splice(1, 1, 88);    // remplacer index 1 par 88

// Trier
[3, 1, 2].sort();              // [1,2,3] (lexicographique!)
[10, 3, 20].sort((a,b)=>a-b); // [3,10,20] ✅ numérique
arr.reverse();               // inverse en place
arr.fill(0, 1, 3);           // remplace [1,3) par 0
Méthodes non-mutantes & utilitaires
// Non-mutantes — retournent un nouveau tableau
[1,2].concat([3,4])   // [1,2,3,4]
arr.slice(1, 3)        // sous-tableau [1,3)
arr.toSorted(…)         // copie triée (ES2023)
arr.toReversed()        // copie inversée (ES2023)
arr.with(1, 99)        // copie avec index 1 = 99
arr.flat(2)             // aplatir sur 2 niveaux
arr.flatMap(x => [x,x]) // map + flat(1)

// Recherche
arr.includes(5)            // true/false
arr.indexOf(5)             // index ou -1
arr.findIndex(x => x > 3)  // premier index valide
arr.findLast(x => x < 5)   // dernier élément (ES2023)

// Info
Array.isArray([])     // true
arr.length            // longueur
arr.join(', ')        // "1, 2, 3"

// Créer des tableaux
Array.from('abc')        // ['a','b','c']
Array.from({length: 5}, (_, i) => i)  // [0,1,2,3,4]
[...new Set([1, 2, 1, 3])]  // [1,2,3] dédupliqué

Objets

Créer et manipuler des objets
// Littéral d'objet
const user = {
  nom: 'Alice',
  age: 25,
  adresse: { ville: 'Paris', cp: '75001' },
  saluer() { return `Je suis ${this.nom}`; },
};

// Accès
user.nom              // dot notation
user['nom']           // bracket notation (dynamique)
const cle = 'age';
user[cle]             // 25 — utile avec des clés variables

// Ajouter / modifier / supprimer
user.email = 'alice@ex.com';  // ajouter
user.age = 26;               // modifier
delete user.adresse;          // supprimer

// Vérifier l'existence d'une clé
'nom' in user             // true
user.hasOwnProperty('nom') // true (propre, pas hérité)
user.nom !== undefined    // moins fiable
Object — méthodes utilitaires
const obj = { a: 1, b: 2, c: 3 };

// Itérer
Object.keys(obj)    // ['a', 'b', 'c']
Object.values(obj)  // [1, 2, 3]
Object.entries(obj) // [['a',1], ['b',2], ['c',3]]

for (const [cle, val] of Object.entries(obj)) {
  console.log(cle, val);
}

// Fusionner des objets
const base    = { a: 1, b: 2 };
const extras  = { b: 99, c: 3 }; // b écrase base.b
const fusionné = { ...base, ...extras }; // {a:1,b:99,c:3}
const f2 = Object.assign({}, base, extras); // idem

// Geler un objet (immuable)
const config = Object.freeze({ version: '1.0' });
config.version = '2.0'; // silencieusement ignoré

// Raccourci propriété (shorthand)
const nom = 'Alice', age = 25;
const user = { nom, age }; // { nom: 'Alice', age: 25 }

// Propriété calculée
const champ = 'email';
const u = { [champ]: 'alice@ex.com' };

Destructuring & spread

Déstructuration
// Tableau
const [a, b, c] = [10, 20, 30];
const [x, , z] = [1, 2, 3];  // sauter le 2ème
const [head, ...tail] = [1, 2, 3, 4]; // rest

// Swap sans variable temporaire
let p = 1, q = 2;
[p, q] = [q, p]; // p=2, q=1

// Objet
const { nom, age } = user;
const { nom: prenom, age: annees } = user; // renommer
const { nom, ville = 'Paris' } = user;   // valeur défaut
const { adresse: { cp } } = user;          // imbriqué

// Rest en objet
const { nom: n, ...reste } = user;
// n = 'Alice', reste = { age:25, email:... }

// Dans les paramètres de fonction
function afficher([a, b], { nom, age = 0 }) { ... }
Spread operator ...
// Copier et fusionner des tableaux
const a = [1, 2], b = [3, 4];
const c = [...a, ...b];        // [1,2,3,4]
const copie = [...a];          // copie indépendante
const avecZero = [0, ...a];   // [0,1,2]

// Copier et fusionner des objets
const base = { a: 1 };
const maj  = { ...base, b: 2 }; // { a:1, b:2 }

// Passer un tableau comme arguments
const nombres = [3, 1, 4, 1, 5];
Math.max(...nombres);   // 5

// Convertir itérables en tableaux
[...new Set([1,2,1])]  // [1,2] — dédupliquer
[...'abc']             // ['a','b','c']
[...document.querySelectorAll('li')] // NodeList → Array

// Mise à jour immuable (pattern Redux)
const state = { user: { nom: 'Alice', score: 0 } };
const newState = {
  ...state,
  user: { ...state.user, score: state.user.score + 1 }
};

Chaînes & template literals

Template literals & méthodes essentielles
// Template literals — backtick
const nom = 'Alice', age = 25;
const msg = `${nom} a ${age} ans`;
const multi = `
  Ligne 1
  Ligne 2
  Résultat : ${2 * age}
`;

// Méthodes de String
'  bonjour  '.trim()          // 'bonjour'
'bonjour'.toUpperCase()      // 'BONJOUR'
'BONJOUR'.toLowerCase()      // 'bonjour'
'bonjour'.includes('jour')   // true
'bonjour'.startsWith('bon')  // true
'bonjour'.endsWith('jour')   // true
'bonjour'.indexOf('j')       // 3
'bonjour'.slice(3, 6)        // 'jou'
'a,b,c'.split(',')          // ['a','b','c']
'ha'.repeat(3)               // 'hahaha'
'5'.padStart(3, '0')         // '005'
'bon'.padEnd(7, '!')         // 'bon!!!!'
'ab-cd'.replaceAll('-', '_') // 'ab_cd'
Regex de base en JS
// Créer une regex
const regex1 = /hello/i;         // littéral
const regex2 = new RegExp('hello', 'i'); // dynamique

// Flags : i=case-insensitive, g=global, m=multiline

// Méthodes de String avec regex
'Bonjour'.match(/jour/)     // ['jour']
'Bonjour'.test              // ← méthode de RegExp
/jour/.test('Bonjour')      // true
'bon-jour'.replace(/-/g, '_') // 'bon_jour'
'a1b2c3'.match(/\d+/g)     // ['1','2','3']

// Validation email simple
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
emailRegex.test('alice@exemple.com'); // true

// Groupes de capture
const date = '2024-03-15';
const match = date.match(/(\d{4})-(\d{2})-(\d{2})/);
// match[1]='2024', match[2]='03', match[3]='15'

// Named groups (ES2018)
const { groups: { annee, mois } } =
  date.match(/(?<annee>\d{4})-(?<mois>\d{2})/);

L'Event Loop

JavaScript est mono-thread — il ne fait qu'une chose à la fois. L'Event Loop permet quand même de gérer des opérations asynchrones (réseau, timers, I/O) sans bloquer le thread.

📚 Call Stack
traiterRésultat(data)
afficher()
main()
Exécution synchrone
LIFO (dernier entré, premier sorti)
🌐 Web APIs
setTimeout(cb, 1000)
fetch('/api/data')
Event listeners
Opérations déléguées
au navigateur/Node
📬 Callback Queue
onFetchComplete
onTimerExpired
Callbacks en attente
FIFO (premier entré, premier sorti)
ℹ️

Fonctionnement : quand la Call Stack est vide, l'Event Loop prend le premier callback de la Queue et le met dans la Stack. La Microtask Queue (Promises) a priorité sur la Macrotask Queue (setTimeout, setInterval) — toutes les microtasks sont vidées avant la prochaine macrotask.

Callbacks

Callbacks — le pattern original
// Un callback = fonction passée en argument,
// appelée quand l'opération est terminée

setTimeout(() => {
  console.log('Affiché après 1 seconde');
}, 1000);

// Pattern Node.js : error-first callback
fs.readFile('fichier.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('Erreur :', err);
    return;
  }
  console.log(data);
});
⚠️

Callback hell : les callbacks imbriqués deviennent vite illisibles. C'est pourquoi les Promises (puis async/await) ont été créées pour aplatir le code asynchrone.

Callback hell — à éviter
// ✗ Pyramid of doom
connexion(user, (err, session) => {
  if (err) { handleError(err); return; }
  chargerProfil(session, (err, profil) => {
    if (err) { handleError(err); return; }
    chargerPréférences(profil, (err, prefs) => {
      if (err) { handleError(err); return; }
      afficherDashboard(session, profil, prefs);
    });
  });
});

// ✅ Avec async/await (voir section suivante)
async function chargerDashboard(user) {
  const session = await connexion(user);
  const profil  = await chargerProfil(session);
  const prefs   = await chargerPréférences(profil);
  afficherDashboard(session, profil, prefs);
}

Promises

Créer et chaîner des Promises
// Créer une Promise
const p = new Promise((resolve, reject) => {
  // opération asynchrone...
  if (succès) resolve(résultat);
  else        reject(new Error('Échec'));
});

// Consommer avec .then().catch().finally()
chargerDonnées()
  .then(data  => traiter(data))
  .then(result => afficher(result))
  .catch(err  => console.error(err))
  .finally(()  => masquerChargement());

// États d'une Promise :
// pending   → en cours
// fulfilled → résolue avec une valeur
// rejected  → rejetée avec une erreur
// settled   → fulfilled ou rejected (définitif)

// Promise.resolve / .reject — raccourcis
Promise.resolve(42).then(v => console.log(v));
Promise.reject(new Error('erreur')).catch(console.error);
Combinateurs de Promises
// Promise.all — attend TOUTES, échoue si 1 échoue
const [users, posts, comments] = await Promise.all([
  fetch('/api/users').then(r => r.json()),
  fetch('/api/posts').then(r => r.json()),
  fetch('/api/comments').then(r => r.json()),
]);
// 3 requêtes en parallèle !

// Promise.allSettled — attend TOUTES, ne fail pas
const résultats = await Promise.allSettled([p1, p2, p3]);
résultats.forEach(r => {
  if (r.status === 'fulfilled') console.log(r.value);
  else console.error(r.reason);
});

// Promise.race — la première qui se termine gagne
const résultat = await Promise.race([
  fetch('/api/data'),
  timeout(5000),  // Promise qui rejette après 5s
]);

// Promise.any — la première résolue (ES2021)
const data = await Promise.any([
  fetchFromServer1(), fetchFromServer2(), fetchFromServer3()
]);
// Prend le plus rapide qui réussit

async / await

Syntaxe et gestion d'erreurs
// async rend une fonction asynchrone
// Elle retourne TOUJOURS une Promise
async function charger() {
  return 42;  // Équivalent à Promise.resolve(42)
}

// await suspend la fonction jusqu'à résolution
// Ne peut s'utiliser que dans une fonction async
async function chargerUtilisateur(id) {
  try {
    const réponse = await fetch(`/api/users/${id}`);

    if (!réponse.ok) {
      throw new Error(`HTTP ${réponse.status}`);
    }

    const user = await réponse.json();
    return user;

  } catch (err) {
    console.error('Erreur réseau :', err);
    throw err; // re-propager si besoin
  } finally {
    masquerLoader();  // toujours exécuté
  }
}

// Arrow function async
const getData = async (url) => {
  const res = await fetch(url);
  return res.json();
};
Pièges et bonnes pratiques
// ✗ Séquentiel inutile (perd l'avantage async)
const users  = await fetchUsers();   // 500ms
const posts  = await fetchPosts();   // 300ms
// Total : 800ms

// ✅ Parallèle avec Promise.all
const [users, posts] = await Promise.all([
  fetchUsers(), fetchPosts()
]);
// Total : ~500ms

// ✗ await dans forEach — ne fonctionne PAS
items.forEach(async (item) => {
  await sauvegarder(item); // ignoré !
});

// ✅ for...of pour await séquentiel
for (const item of items) {
  await sauvegarder(item);
}

// ✅ Promise.all pour parallèle
await Promise.all(items.map(sauvegarder));

// Top-level await (modules ES2022)
const config = await fetch('/config.json').then(r => r.json());
// ← Directement au niveau du module, sans async fn

Manipulation du DOM

Sélectionner des éléments
// Sélectionner
document.querySelector('#btn')         // 1 élément
document.querySelectorAll('.carte')    // NodeList
document.getElementById('titre')      // par id
document.getElementsByClassName('x')  // HTMLCollection (live)

// Navigation dans le DOM
el.parentElement
el.children           // éléments enfants
el.firstElementChild
el.lastElementChild
el.nextElementSibling
el.previousElementSibling
el.closest('.parent') // ancêtre le plus proche

// Modifier le contenu
el.textContent = 'Nouveau texte';   // sûr
el.innerHTML   = '<b>Gras</b>';   // XSS si user input !

// Attributs
el.getAttribute('href')
el.setAttribute('href', 'https://...')
el.removeAttribute('disabled')
el.hasAttribute('required')
el.dataset.userId   // <el data-user-id="42">
Créer, modifier, supprimer
// Classes CSS
el.classList.add('actif')
el.classList.remove('actif')
el.classList.toggle('ouvert')
el.classList.contains('visible')
el.classList.replace('old', 'new')

// Styles inline
el.style.color = 'red';
el.style.display = 'none';
el.style.setProperty('--color', 'blue');

// Créer et insérer des éléments
const li = document.createElement('li');
li.textContent = 'Nouvel élément';
li.className = 'item';

ul.appendChild(li);
ul.prepend(li);
ul.insertBefore(li, autreEl);
el.insertAdjacentHTML('beforeend', '<li>...</li>');

// Supprimer
el.remove();
parent.removeChild(el);

// Cloner
const copie = el.cloneNode(true); // true = deep clone

// Mesures
el.getBoundingClientRect() // position + dimensions
el.offsetWidth / offsetHeight
el.scrollTop / scrollLeft

Événements

addEventListener & événements courants
// Ajouter un listener
btn.addEventListener('click', (e) => {
  console.log('Cliqué !', e.target);
});

// Supprimer (nécessite une référence nommée)
const handler = (e) => console.log(e);
btn.addEventListener('click', handler);
btn.removeEventListener('click', handler);

// L'objet Event
e.target        // élément cliqué
e.currentTarget // élément sur lequel le listener est
e.preventDefault()  // empêcher comportement par défaut
e.stopPropagation() // arrêter la propagation

// Clavier
input.addEventListener('keydown', (e) => {
  if (e.key === 'Enter') soumettre();
  if (e.key === 'Escape') fermer();
  e.ctrlKey  // Ctrl enfoncé
  e.shiftKey // Shift enfoncé
});

// Délégation d'événements
// Au lieu d'ajouter un listener sur chaque li :
ul.addEventListener('click', (e) => {
  const li = e.target.closest('li');
  if (!li) return;
  li.classList.toggle('selectionné');
});
Événements courants
// Souris
click, dblclick, mouseenter, mouseleave
mouseover, mouseout, mousemove
mousedown, mouseup, contextmenu

// Clavier
keydown, keyup, keypress (déprécié)

// Formulaire
submit, change, input, focus, blur, reset

// Document / Fenêtre
DOMContentLoaded  // HTML parsé (sans attendre images)
load              // tout chargé (images comprises)
resize, scroll

// Touch (mobile)
touchstart, touchmove, touchend

// Attendre que le DOM soit prêt
document.addEventListener('DOMContentLoaded', () => {
  // Code JS ici — DOM disponible
});

// Debounce — limiter la fréquence d'appel
function debounce(fn, delay) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}
window.addEventListener('resize', debounce(handler, 200));

Fetch API

GET, POST, PUT, DELETE
// GET — récupérer des données
async function getUsers() {
  const res = await fetch('/api/users');
  if (!res.ok) throw new Error(res.status);
  return res.json();
}

// POST — envoyer des données JSON
async function createUser(userData) {
  const res = await fetch('/api/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(userData),
  });
  return res.json();
}

// PUT / PATCH / DELETE
await fetch(`/api/users/${id}`, {
  method: 'DELETE',
  headers: { 'Authorization': `Bearer ${token}` },
});
Wrapper fetch robuste
// Fonction utilitaire avec gestion d'erreurs
async function api(endpoint, options = {}) {
  const defaults = {
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${getToken()}`,
    },
  };

  const config = {
    ...defaults,
    ...options,
    headers: { ...defaults.headers, ...options.headers },
  };

  const res = await fetch(`/api${endpoint}`, config);

  if (!res.ok) {
    const errData = await res.json().catch(() => ({}));
    throw new Error(errData.message || `HTTP ${res.status}`);
  }

  if (res.status === 204) return null; // No Content
  return res.json();
}

// Utilisation propre
const user    = await api('/users/42');
const created = await api('/users', {
  method: 'POST',
  body: JSON.stringify({ nom: 'Alice' }),
});

Cheat sheet

Variables & types

constPar défaut — référence constante
letSi la valeur change
varJamais — scope fonction, hoisting
===Toujours (jamais ==)
typeof null"object" ← bug historique
?? vs ||?? : null/undef seulement

Tableaux — méthodes clés

map(fn)Transformer → nouveau tableau
filter(fn)Filtrer → sous-tableau
reduce(fn, init)Accumuler → valeur unique
find(fn)Premier élément trouvé
some/everyAu moins un / tous
flat/flatMapAplatir / map + flat

Asynchrone

async fnRetourne une Promise
awaitSuspend jusqu'à résolution
try/catchGérer les erreurs async
Promise.allTout en parallèle
Promise.allSettledTout, même si erreur
await en forEachNe fonctionne PAS

DOM & événements

querySelector1er élément CSS selector
querySelectorAllTous les éléments
addEventListenerAttacher un événement
e.targetÉlément déclencheur
e.preventDefault()Annuler défaut navigateur
DélégationListener sur parent