Breaking

mardi 31 mars 2026

Cours Essentiels - Préparation Examen

Cours Essentiels - Préparation Examen

📚 Cours Essentiels

Préparation Examen QCM - Développement Web & Programmation

🎨 HTML & CSS

Différence fondamentale : HTML = contenu (QUOI afficher) | CSS = apparence (COMMENT l'afficher)

Structure HTML5

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Titre</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <!-- Contenu visible -->
</body>
</html>

Balises Sémantiques HTML5

Balise Usage vs <div>
<header> En-tête page/section Sémantique
<nav> Navigation principale uniquement Sémantique
<main> Contenu principal UNIQUE (1/page) Sémantique
<article> Contenu AUTONOME, réutilisable Sémantique
<section> Section thématique (avec titre) Sémantique
⚠️ Piège QCM : <strong><b> | <em><i>
<strong> et <em> = valeur sémantique (SEO, accessibilité)
<b> et <i> = purement décoratif (déprécié)

Différence ID vs Class

ID Class
Unicité UNIQUE par page RÉUTILISABLE
Syntaxe CSS #nom .nom
Syntaxe HTML id="nom" class="nom1 nom2"
Spécificité 100 10

Box Model

Ordre : MARGIN (extérieur) → BORDERPADDING (intérieur) → CONTENT
box-sizing: border-box recommandé : width inclut padding + border

Unités CSS

Unité Type Description
px Absolue Pixels (fixe)
% Relative Pourcentage parent
em Relative Taille relative PARENT (composé)
rem Relative Taille relative RACINE (prévisible) ✅
vw/vh Viewport 1% largeur/hauteur viewport

Flexbox Essentials

.container {
    display: flex;
    flex-direction: row;        /* row | column */
    justify-content: center;     /* axe principal */
    align-items: center;         /* axe transversal */
    flex-wrap: wrap;
    gap: 20px;
}
Centre parfait : justify-content: center + align-items: center

Media Queries (Mobile First)

.element { width: 100%; }        /* Mobile */

@media (min-width: 768px) {      /* Tablette */
    .element { width: 50%; }
}

@media (min-width: 1024px) {     /* Desktop */
    .element { width: 33.33%; }
}

⚡ JavaScript

Variables

Mot clé Caractéristique Usage
var Ancien, portée fonction, hoisting ❌ À éviter
let Moderne, modifiable, portée bloc ✅ Variables changeantes
const Moderne, constante, portée bloc ✅ Valeurs fixes
Exemple pratique :
// var - portée fonction (problematique)
var x = 10;
if (true) {
    var x = 20; // Même variable !
}
console.log(x); // Résultat : 20

// let - portée bloc
let y = 10;
if (true) {
    let y = 20; // Variable différente
}
console.log(y); // Résultat : 10

// const - ne peut pas être réassignée
const PI = 3.14;
PI = 3.14159; // ❌ Erreur : Assignment to constant variable

Types de Données

Type Exemple typeof
String "Bonjour" ou 'Bonjour' "string"
Number 42, 3.14, Infinity, NaN "number"
Boolean true ou false "boolean"
Undefined let x; (non initialisé) "undefined"
Null let x = null; (vide intentionnel) "object" ⚠️
Object {nom: "Ali", age: 25} "object"
Array [1, 2, 3] "object"
Function function() {} "function"
Symbol Symbol("id") "symbol"
BigInt 9007199254740991n "bigint"
Exemple - Vérification des types :
// Strings
let nom = "Alice";
console.log(typeof nom); // Résultat : "string"

// Numbers
let age = 25;
let prix = 19.99;
console.log(typeof age);  // Résultat : "number"
console.log(typeof prix); // Résultat : "number"

// Boolean
let estActif = true;
console.log(typeof estActif); // Résultat : "boolean"

// Undefined - variable déclarée mais non initialisée
let x;
console.log(x);        // Résultat : undefined
console.log(typeof x); // Résultat : "undefined"

// Null - valeur vide intentionnelle
let y = null;
console.log(y);        // Résultat : null
console.log(typeof y); // Résultat : "object" ⚠️ (bug JS historique)

// Object
let personne = { nom: "Ali", age: 30 };
console.log(typeof personne); // Résultat : "object"

// Array (tableau)
let couleurs = ["rouge", "vert", "bleu"];
console.log(typeof couleurs);        // Résultat : "object"
console.log(Array.isArray(couleurs)); // Résultat : true

// Function
function direBonjour() { return "Salut!"; }
console.log(typeof direBonjour); // Résultat : "function"
⚠️ Types Primitifs vs Référence :
Primitifs (copie de valeur) : String, Number, Boolean, undefined, null, Symbol, BigInt
Référence (copie de référence) : Object, Array, Function
Exemple - Primitif vs Référence :
// TYPE PRIMITIF - copie de valeur
let a = 5;
let b = a;  // Copie la valeur
b = 10;
console.log(a); // Résultat : 5 (inchangé)
console.log(b); // Résultat : 10

// TYPE RÉFÉRENCE - copie de référence
let obj1 = { nom: "Ali" };
let obj2 = obj1;  // Copie la référence (même objet)
obj2.nom = "Sara";
console.log(obj1.nom); // Résultat : "Sara" (modifié !)
console.log(obj2.nom); // Résultat : "Sara"

// Solution pour copier un objet
let obj3 = { ...obj1 };  // Spread operator
let obj4 = JSON.parse(JSON.stringify(obj1)); // Deep copy

Opérateurs Arithmétiques

Opérateur Description Exemple
+ Addition / Concaténation 5 + 3 → 8
- Soustraction 5 - 3 → 2
* Multiplication 5 * 3 → 15
/ Division 6 / 2 → 3
% Modulo (reste division) 7 % 3 → 1
** Puissance (ES6) 2 ** 3 → 8
++ Incrémentation let x = 5; x++ → 6
-- Décrémentation let x = 5; x-- → 4
Exemple pratique :
// Addition
let somme = 10 + 5;
console.log(somme); // Résultat : 15

// Concaténation (attention avec + et strings)
let texte = "Hello" + " " + "World";
console.log(texte); // Résultat : "Hello World"

console.log("5" + 3);  // Résultat : "53" (concaténation !)
console.log(5 + "3");  // Résultat : "53"

// Modulo - très utile pour vérifier pair/impair
console.log(10 % 2); // Résultat : 0 (pair)
console.log(7 % 2);  // Résultat : 1 (impair)

// Puissance
console.log(2 ** 10); // Résultat : 1024

// Incrémentation
let x = 5;
console.log(x++); // Affiche 5, puis x devient 6
console.log(x);   // Résultat : 6

let y = 5;
console.log(++y); // Incrémente d'abord, affiche 6

Opérateurs de Comparaison

Opérateur Description Exemple
== Égalité valeur (avec conversion) 5 == "5" → true
=== Égalité stricte (valeur + type) 5 === "5" → false
!= Différent (avec conversion) 5 != "5" → false
!== Différent strict 5 !== "5" → true
>, < Supérieur / Inférieur 5 > 3 → true
>=, <= Supérieur ou égal / Inférieur ou égal 5 >= 5 → true
Exemple - Comparaisons :
// == vs === (TRÈS IMPORTANT)
console.log(5 == "5");   // Résultat : true (conversion automatique)
console.log(5 === "5");  // Résultat : false (types différents)

console.log(0 == false);   // Résultat : true
console.log(0 === false);  // Résultat : false

console.log(null == undefined);   // Résultat : true
console.log(null === undefined);  // Résultat : false

// != vs !==
console.log(5 != "5");   // Résultat : false
console.log(5 !== "5");  // Résultat : true

// Comparaisons numériques
console.log(10 > 5);   // Résultat : true
console.log(10 >= 10); // Résultat : true
console.log(3 < 2);    // Résultat : false

// Comparaison de strings
console.log("a" < "b");     // Résultat : true
console.log("abc" < "abd"); // Résultat : true
⚠️ Différence critique : == vs ===
== : égalité valeur (conversion implicite) → 5 == "5" : true
=== : égalité valeur + type (STRICTE) → 5 === "5" : false

Opérateurs Logiques

Opérateur Description Exemple
&& ET logique true && false → false
|| OU logique true || false → true
! NON logique !true → false
?? Coalescence nulle (ES2020) null ?? "default" → "default"
Exemple - Opérateurs logiques :
// && (ET) - retourne première valeur falsy ou la dernière
console.log(true && true);   // Résultat : true
console.log(true && false);  // Résultat : false
console.log(false && true);  // Résultat : false (court-circuit)
console.log(5 && 10);        // Résultat : 10
console.log(0 && 10);        // Résultat : 0 (falsy)
console.log("Ali" && "Sara"); // Résultat : "Sara"

// || (OU) - retourne première valeur truthy ou la dernière
console.log(true || false);   // Résultat : true
console.log(false || true);   // Résultat : true
console.log(false || false);  // Résultat : false
console.log(0 || 10);         // Résultat : 10
console.log("" || "default"); // Résultat : "default"

// ! (NON)
console.log(!true);   // Résultat : false
console.log(!false);  // Résultat : true
console.log(!0);      // Résultat : true
console.log(!!5);     // Résultat : true (conversion en booléen)

// ?? (Nullish Coalescing) - seulement null/undefined
console.log(null ?? "default");      // Résultat : "default"
console.log(undefined ?? "default"); // Résultat : "default"
console.log(0 ?? "default");         // Résultat : 0
console.log("" ?? "default");        // Résultat : ""
Court-circuit :
&& : retourne première valeur falsy ou dernière valeur
|| : retourne première valeur truthy ou dernière valeur
?? : retourne première valeur définie (non null/undefined)

Structures Conditionnelles

Structure Syntaxe Usage
if / else if / else if (condition) {} else {} Conditions classiques
switch switch(val) { case x: break; } Comparaison multiple valeurs
Ternaire condition ? vrai : faux Conditions simples en une ligne
Exemple - if / else if / else :
let note = 75;

if (note >= 90) {
    console.log("Excellent");
} else if (note >= 80) {
    console.log("Très bien");
} else if (note >= 70) {
    console.log("Bien");
} else if (note >= 60) {
    console.log("Passable");
} else {
    console.log("Insuffisant");
}
// Résultat : "Bien"
Exemple - switch :
let jour = 3;
let nomJour;

switch (jour) {
    case 1:
        nomJour = "Lundi";
        break;  // Important !
    case 2:
        nomJour = "Mardi";
        break;
    case 3:
        nomJour = "Mercredi";
        break;
    case 4:
        nomJour = "Jeudi";
        break;
    case 5:
        nomJour = "Vendredi";
        break;
    default:
        nomJour = "Weekend";
}
console.log(nomJour); // Résultat : "Mercredi"

// Sans break (fall-through)
let x = 1;
switch (x) {
    case 1:
        console.log("Un");
        // pas de break !
    case 2:
        console.log("Deux");
        break;
}
// Résultat : "Un" puis "Deux" (fall-through)
Exemple - Opérateur ternaire :
let age = 20;

// if/else classique
let message;
if (age >= 18) {
    message = "Majeur";
} else {
    message = "Mineur";
}

// Équivalent avec ternaire
let message2 = age >= 18 ? "Majeur" : "Mineur";
console.log(message2); // Résultat : "Majeur"

// Ternaire imbriqué (à éviter pour la lisibilité)
let score = 85;
let resultat = score >= 90 ? "A" : score >= 80 ? "B" : "C";
console.log(resultat); // Résultat : "B"
⚠️ Switch : ne pas oublier break !
Sans break, l'exécution continue dans les cas suivants (fall-through)

Boucles

Boucle Syntaxe Usage
for for (let i=0; i<n; i++) {} Nombre d'itérations connu
while while (condition) {} Condition d'arrêt, itérations inconnues
do...while do {} while (condition) Au moins une exécution
for...of for (let item of array) {} Parcourir valeurs d'un itérable
for...in for (let key in obj) {} Parcourir clés d'un objet
Exemple - Boucle for :
// Boucle for classique
for (let i = 0; i < 5; i++) {
    console.log(i);
}
// Résultat : 0, 1, 2, 3, 4

// Parcourir un tableau
let fruits = ["pomme", "banane", "orange"];
for (let i = 0; i < fruits.length; i++) {
    console.log(fruits[i]);
}
// Résultat : "pomme", "banane", "orange"

// Boucle inversée
for (let i = 5; i > 0; i--) {
    console.log(i);
}
// Résultat : 5, 4, 3, 2, 1
Exemple - while et do...while :
// while - condition vérifiée AVANT
let i = 0;
while (i < 3) {
    console.log(i);
    i++;
}
// Résultat : 0, 1, 2

// do...while - exécution AU MOINS une fois
let j = 10;
do {
    console.log(j);
    j++;
} while (j < 3);
// Résultat : 10 (exécuté une fois même si condition fausse)

// Exemple pratique : deviner un nombre
let secret = 7;
let essai = 0;
while (essai !== secret) {
    essai++; // simulation
}
console.log("Trouvé !"); // Résultat : "Trouvé !"
Exemple - for...of vs for...in :
// for...of - valeurs d'un tableau
let couleurs = ["rouge", "vert", "bleu"];
for (let couleur of couleurs) {
    console.log(couleur);
}
// Résultat : "rouge", "vert", "bleu"

// for...of avec string
for (let char of "Hello") {
    console.log(char);
}
// Résultat : "H", "e", "l", "l", "o"

// for...in - clés d'un objet
let personne = { nom: "Ali", age: 25, ville: "Paris" };
for (let cle in personne) {
    console.log(cle + ": " + personne[cle]);
}
// Résultat : "nom: Ali", "age: 25", "ville: Paris"

// for...in sur tableau (déconseillé)
let arr = ["a", "b", "c"];
for (let index in arr) {
    console.log(index); // "0", "1", "2" (strings !)
}
for...of vs for...in :
for...of : valeurs (tableaux, strings, Map, Set)
for...in : clés/indices (objets, tableaux - évitez pour tableaux)

Fonctions

Type Syntaxe Caractéristique
Déclaration function nom(params) { return x; } Hoisted (utilisable avant déclaration)
Expression const fn = function() {}; Non hoisted
Fléchée const fn = (params) => x; Pas de this propre
IIFE (function() { })(); Exécutée immédiatement
Exemple - Déclaration de fonction :
// Déclaration (hoisted)
console.log(saluer("Ali")); // Résultat : "Bonjour Ali"

function saluer(nom) {
    return "Bonjour " + nom;
}

// Expression de fonction (non hoisted)
const direAuRevoir = function(nom) {
    return "Au revoir " + nom;
};
console.log(direAuRevoir("Ali")); // Résultat : "Au revoir Ali"
Exemple - Fonctions fléchées :
// Syntaxe complète
const addition = (a, b) => {
    return a + b;
};
console.log(addition(5, 3)); // Résultat : 8

// Syntaxe raccourcie (return implicite)
const multiplier = (a, b) => a * b;
console.log(multiplier(4, 3)); // Résultat : 12

// Un seul paramètre - parenthèses optionnelles
const double = x => x * 2;
console.log(double(5)); // Résultat : 10

// Aucun paramètre
const direBonjour = () => "Hello!";
console.log(direBonjour()); // Résultat : "Hello!"

// Retourner un objet
const creerPersonne = (nom, age) => ({ nom, age });
console.log(creerPersonne("Ali", 25)); // Résultat : {nom: "Ali", age: 25}
Exemple - IIFE (Immediately Invoked Function Expression) :
// IIFE - exécutée immédiatement
(function() {
    let secret = "hidden";
    console.log("Exécuté immédiatement : " + secret);
})();
// Résultat : "Exécuté immédiatement : hidden"

// IIFE avec paramètre
(function(nom) {
    console.log("Bonjour " + nom);
})("Ali");
// Résultat : "Bonjour Ali"

// IIFE fléchée
(() => {
    console.log("IIFE fléchée");
})();
// Résultat : "IIFE fléchée"
⚠️ Fonctions fléchées et this :
Les arrow functions n'ont pas leur propre this. Elles héritent du contexte parent.
Ne PAS utiliser comme méthodes d'objet ou constructeurs.
Exemple - this avec fonctions fléchées :
// PROBLÈME avec fonction classique
const personne1 = {
    nom: "Ali",
    saluer: function() {
        setTimeout(function() {
            console.log("Bonjour " + this.nom); // this = window
        }, 100);
    }
};
personne1.saluer(); // Résultat : "Bonjour undefined"

// SOLUTION avec arrow function
const personne2 = {
    nom: "Ali",
    saluer: function() {
        setTimeout(() => {
            console.log("Bonjour " + this.nom); // this hérité
        }, 100);
    }
};
personne2.saluer(); // Résultat : "Bonjour Ali"

// ❌ NE PAS utiliser arrow function comme méthode
const personne3 = {
    nom: "Ali",
    saluer: () => console.log(this.nom)
};
personne3.saluer(); // Résultat : undefined

Paramètres de Fonctions

Type Exemple Description
Par défaut function(a = 10) {} Valeur si argument non fourni
Rest function(...args) {} Nombre variable d'arguments
Destructuring function({a, b}) {} Extraire propriétés objet
Exemple - Paramètres par défaut :
// Paramètres par défaut
function saluer(nom = "Invité", titre = "M.") {
    return "Bonjour " + titre + " " + nom;
}

console.log(saluer());              // Résultat : "Bonjour M. Invité"
console.log(saluer("Ali"));         // Résultat : "Bonjour M. Ali"
console.log(saluer("Sara", "Mme")); // Résultat : "Bonjour Mme Sara"

// Avec arrow function
const calculer = (a, b = 10) => a + b;
console.log(calculer(5));    // Résultat : 15
console.log(calculer(5, 3)); // Résultat : 8
Exemple - Rest parameters :
// Rest parameters - nombre variable d'arguments
function somme(...nombres) {
    return nombres.reduce((acc, val) => acc + val, 0);
}

console.log(somme(1, 2, 3));    // Résultat : 6
console.log(somme(1, 2, 3, 4)); // Résultat : 10
console.log(somme(10));         // Résultat : 10

// Combiné avec autres paramètres
function logger(niveau, ...messages) {
    console.log("[" + niveau + "] " + messages.join(", "));
}
logger("ERROR", "Fichier non trouvé", "Ligne 42");
// Résultat : "[ERROR] Fichier non trouvé, Ligne 42"
Exemple - Destructuring en paramètre :
// Destructuring d'objet
function afficherUtilisateur({ nom, age, ville = "Inconnu" }) {
    console.log(nom + ", " + age + " ans, " + ville);
}

afficherUtilisateur({ nom: "Ali", age: 25, ville: "Paris" });
// Résultat : "Ali, 25 ans, Paris"

afficherUtilisateur({ nom: "Sara", age: 30 });
// Résultat : "Sara, 30 ans, Inconnu"

// Destructuring de tableau
function premierEtDernier([premier, ...reste]) {
    const dernier = reste.pop();
    return { premier, dernier };
}

console.log(premierEtDernier([1, 2, 3, 4, 5]));
// Résultat : { premier: 1, dernier: 5 }

Portée (Scope)

Portée Description Exemple
Globale Accessible partout Variables hors fonction
Fonction Accessible dans la fonction var dans une fonction
Bloc Accessible dans le bloc let/const dans {}
Exemple - Portées :
// Variable globale
var globale = "Je suis globale";
let globale2 = "Moi aussi";

function exemple() {
    // Variable de fonction (var)
    var localeFonction = "Dans la fonction";
    
    if (true) {
        // Variable de bloc (let/const)
        let localeBloc = "Dans le bloc";
        const CONSTANTE = "Fixe";
        console.log(localeFonction); // OK
        console.log(localeBloc);     // OK
    }
    
    console.log(localeFonction); // OK
    // console.log(localeBloc);  // ❌ Erreur
}

exemple();
console.log(globale); // OK
// console.log(localeFonction); // ❌ Erreur
Hoisting :
Déclarations remontées au début de leur portée.
var : hoisted avec valeur undefined
let/const : hoisted mais TDZ (Temporal Dead Zone)
Exemple - Hoisting :
// var - hoisted avec undefined
console.log(x); // Résultat : undefined (pas d'erreur)
var x = 5;
console.log(x); // Résultat : 5

// let/const - TDZ (Temporal Dead Zone)
// console.log(y); // ❌ ReferenceError
let y = 10;

// Function declaration - complètement hoisted
saluer(); // Résultat : "Bonjour"
function saluer() {
    console.log("Bonjour");
}

// Function expression - PAS hoisted
// direAuRevoir(); // ❌ Erreur
const direAuRevoir = function() {
    console.log("Au revoir");
};

Objets

Opération Syntaxe Exemple
Création { clé: valeur } { nom: "Ali", age: 25 }
Accès notation point obj.clé person.nom
Accès notation crochet obj["clé"] person["nom"]
Ajout/Modification obj.clé = valeur person.ville = "Paris"
Suppression delete obj.clé delete person.age
Vérifier clé "clé" in obj "nom" in person
Exemple - Manipulation d'objets :
// Création
const personne = {
    nom: "Ali",
    age: 25,
    ville: "Paris"
};
console.log(personne);
// Résultat : { nom: "Ali", age: 25, ville: "Paris" }

// Accès notation point
console.log(personne.nom);  // Résultat : "Ali"
console.log(personne.age);  // Résultat : 25

// Accès notation crochet (utile pour clés dynamiques)
console.log(personne["nom"]); // Résultat : "Ali"
let cle = "ville";
console.log(personne[cle]);   // Résultat : "Paris"

// Ajout de propriété
personne.email = "ali@email.com";
console.log(personne.email); // Résultat : "ali@email.com"

// Modification
personne.age = 26;
console.log(personne.age); // Résultat : 26

// Suppression
delete personne.ville;
console.log(personne);
// Résultat : { nom: "Ali", age: 26, email: "ali@email.com" }

// Vérifier existence
console.log("nom" in personne);    // Résultat : true
console.log("ville" in personne);  // Résultat : false

Méthodes d'Objets Importantes

Méthode Description Retourne
Object.keys(obj) Liste des clés Tableau de clés
Object.values(obj) Liste des valeurs Tableau de valeurs
Object.entries(obj) Paires clé-valeur Tableau de tableaux
Object.assign(target, ...sources) Fusionne objets Objet modifié
Object.freeze(obj) Gèle l'objet Objet non modifiable
Exemple - Méthodes Object :
const personne = { nom: "Ali", age: 25, ville: "Paris" };

// Object.keys() - clés
console.log(Object.keys(personne));
// Résultat : ["nom", "age", "ville"]

// Object.values() - valeurs
console.log(Object.values(personne));
// Résultat : ["Ali", 25, "Paris"]

// Object.entries() - paires
console.log(Object.entries(personne));
// Résultat : [["nom", "Ali"], ["age", 25], ["ville", "Paris"]]

// Object.assign() - fusion
const defaults = { theme: "dark", lang: "fr" };
const userPrefs = { theme: "light" };
const config = Object.assign({}, defaults, userPrefs);
console.log(config);
// Résultat : { theme: "light", lang: "fr" }

// Object.freeze() - geler
const constantes = Object.freeze({ PI: 3.14 });
constantes.PI = 3.14159; // Ignoré silencieusement (ou erreur en strict)
console.log(constantes.PI); // Résultat : 3.14

Méthodes Strings Importantes

Méthode Exemple Résultat
length "Hello".length 5
toUpperCase() "hello".toUpperCase() "HELLO"
toLowerCase() "HELLO".toLowerCase() "hello"
charAt(index) "Hello".charAt(0) "H"
slice(start, end) "Hello".slice(1, 4) "ell" (end exclus)
substring(start, end) "Hello".substring(1, 4) "ell"
indexOf() "Hello".indexOf("l") 2 (première occurrence)
lastIndexOf() "Hello".lastIndexOf("l") 3 (dernière occurrence)
includes() "Hello".includes("el") true
startsWith() "Hello".startsWith("He") true
endsWith() "Hello".endsWith("lo") true
split(séparateur) "a,b,c".split(",") ["a", "b", "c"]
trim() " hi ".trim() "hi"
replace(a, b) "hello".replace("l", "L") "heLlo" (premier uniquement)
replaceAll(a, b) "hello".replaceAll("l", "L") "heLLo"
repeat(n) "ab".repeat(3) "ababab"
Exemple - Méthodes Strings :
let texte = "  Hello World  ";

// Length et casse
console.log(texte.length);            // Résultat : 14
console.log(texte.toUpperCase());     // Résultat : "  HELLO WORLD  "
console.log(texte.toLowerCase());     // Résultat : "  hello world  "

// Trim - supprime espaces
console.log(texte.trim());            // Résultat : "Hello World"

// Accès aux caractères
console.log("Hello".charAt(0));       // Résultat : "H"
console.log("Hello"[0]);              // Résultat : "H"

// Recherche
let phrase = "JavaScript est génial";
console.log(phrase.indexOf("est"));     // Résultat : 11
console.log(phrase.includes("Java"));   // Résultat : true
console.log(phrase.startsWith("Java")); // Résultat : true
console.log(phrase.endsWith("génial")); // Résultat : true

// Extraction
console.log("Hello World".slice(0, 5));  // Résultat : "Hello"
console.log("Hello World".slice(-5));    // Résultat : "World"

// Split et replace
let csv = "pomme,banane,orange";
let fruits = csv.split(",");
console.log(fruits); // Résultat : ["pomme", "banane", "orange"]

console.log("hello world".replace("o", "0"));
// Résultat : "hell0 world" (premier seulement)

console.log("hello world".replaceAll("o", "0"));
// Résultat : "hell0 w0rld"

Tableaux (Arrays)

Opération Syntaxe Exemple
Création [elem1, elem2] [1, 2, 3]
Accès arr[index] arr[0] → premier élément
Modification arr[index] = val arr[0] = 10
Longueur arr.length [1,2,3].length → 3
Exemple - Manipulation basique :
// Création
let nombres = [1, 2, 3, 4, 5];
let fruits = ["pomme", "banane", "orange"];
let mixte = [1, "deux", true, null];

// Accès
console.log(fruits[0]);  // Résultat : "pomme"
console.log(fruits[1]);  // Résultat : "banane"
console.log(fruits[-1]); // Résultat : undefined (pas Python !)
console.log(fruits.at(-1)); // Résultat : "orange" (ES2022)

// Longueur
console.log(fruits.length); // Résultat : 3

// Modification
fruits[1] = "fraise";
console.log(fruits); // Résultat : ["pomme", "fraise", "orange"]

// Ajout dynamique
fruits[3] = "kiwi";
console.log(fruits); // Résultat : ["pomme", "fraise", "orange", "kiwi"]

// Parcourir
for (let i = 0; i < fruits.length; i++) {
    console.log(fruits[i]);
}
// Résultat : "pomme", "fraise", "orange", "kiwi"

Méthodes Arrays - Ajout/Suppression

Méthode Action Modifie original ?
push(elem) Ajoute à la fin ✅ Oui
pop() Supprime et retourne le dernier ✅ Oui
unshift(elem) Ajoute au début ✅ Oui
shift() Supprime et retourne le premier ✅ Oui
splice(index, n, ...items) Supprime/ajoute à un index ✅ Oui
slice(start, end) Extrait une portion ❌ Non
Exemple - push, pop, shift, unshift :
let fruits = ["pomme", "banane"];

// push - ajoute à la fin
fruits.push("orange");
console.log(fruits); // Résultat : ["pomme", "banane", "orange"]

let longueur = fruits.push("kiwi");
console.log(longueur); // Résultat : 4 (nouvelle longueur)

// pop - supprime le dernier
let dernier = fruits.pop();
console.log(dernier); // Résultat : "kiwi"
console.log(fruits);  // Résultat : ["pomme", "banane", "orange"]

// unshift - ajoute au début
fruits.unshift("fraise");
console.log(fruits); // Résultat : ["fraise", "pomme", "banane", "orange"]

// shift - supprime le premier
let premier = fruits.shift();
console.log(premier); // Résultat : "fraise"
console.log(fruits);  // Résultat : ["pomme", "banane", "orange"]
Exemple - slice vs splice :
// slice - NE MODIFIE PAS l'original
let nombres = [1, 2, 3, 4, 5];
let extrait = nombres.slice(1, 4);
console.log(extrait);  // Résultat : [2, 3, 4]
console.log(nombres);  // Résultat : [1, 2, 3, 4, 5] (inchangé)

// slice avec négatifs
console.log(nombres.slice(-2)); // Résultat : [4, 5]

// splice - MODIFIE l'original
let lettres = ["a", "b", "c", "d", "e"];
let supprimes = lettres.splice(2, 2); // index 2, supprimer 2 éléments
console.log(supprimes); // Résultat : ["c", "d"]
console.log(lettres);   // Résultat : ["a", "b", "e"]

// splice pour ajouter
let arr = [1, 2, 5];
arr.splice(2, 0, 3, 4); // index 2, supprimer 0, ajouter 3 et 4
console.log(arr); // Résultat : [1, 2, 3, 4, 5]

// splice pour remplacer
let arr2 = [1, 2, 3, 4];
arr2.splice(1, 2, "a", "b"); // supprime 2 et 3, ajoute a et b
console.log(arr2); // Résultat : [1, "a", "b", 4]
⚠️ Différence slice vs splice (QCM fréquent) :
slice() : NE MODIFIE PAS l'original, retourne extrait
splice() : MODIFIE l'original, supprime/ajoute éléments

Méthodes Arrays - Recherche

Méthode Description Retourne
indexOf(elem) Premier index de l'élément Index ou -1
lastIndexOf(elem) Dernier index de l'élément Index ou -1
includes(elem) Vérifie présence Boolean
find(callback) Premier élément qui satisfait condition Élément ou undefined
findIndex(callback) Index du premier élément satisfaisant Index ou -1
Exemple - Recherche dans tableaux :
let fruits = ["pomme", "banane", "orange", "banane"];

// indexOf / lastIndexOf
console.log(fruits.indexOf("banane"));     // Résultat : 1
console.log(fruits.lastIndexOf("banane")); // Résultat : 3
console.log(fruits.indexOf("kiwi"));       // Résultat : -1

// includes
console.log(fruits.includes("orange")); // Résultat : true
console.log(fruits.includes("kiwi"));   // Résultat : false

// find - trouver un élément selon condition
let nombres = [5, 12, 8, 130, 44];
let premierGrand = nombres.find(n => n > 10);
console.log(premierGrand); // Résultat : 12

// find avec objets
let utilisateurs = [
    { id: 1, nom: "Ali" },
    { id: 2, nom: "Sara" },
    { id: 3, nom: "Omar" }
];
let user = utilisateurs.find(u => u.id === 2);
console.log(user); // Résultat : { id: 2, nom: "Sara" }

// findIndex
let index = utilisateurs.findIndex(u => u.nom === "Omar");
console.log(index); // Résultat : 2

Méthodes Arrays - Transformation

Méthode Action Modifie original ?
map(callback) Transforme chaque élément ❌ Non (nouveau tableau)
filter(callback) Filtre selon condition ❌ Non (nouveau tableau)
reduce(callback, init) Réduit à une seule valeur ❌ Non
sort(compareFn) Trie le tableau ✅ Oui
reverse() Inverse l'ordre ✅ Oui
flat(depth) Aplatit tableau imbriqué ❌ Non
flatMap(callback) map + flat ❌ Non
Exemple - map :
let nombres = [1, 2, 3, 4, 5];

// Doubler chaque nombre
let doubles = nombres.map(n => n * 2);
console.log(doubles);  // Résultat : [2, 4, 6, 8, 10]
console.log(nombres);  // Résultat : [1, 2, 3, 4, 5] (inchangé)

// Extraire une propriété
let users = [
    { nom: "Ali", age: 25 },
    { nom: "Sara", age: 30 },
    { nom: "Omar", age: 20 }
];
let noms = users.map(u => u.nom);
console.log(noms); // Résultat : ["Ali", "Sara", "Omar"]

// Transformer en HTML
let items = ["Pommes", "Bananes", "Oranges"];
let html = items.map(item => `
  • ${item}
  • `); console.log(html); // Résultat : ["
  • Pommes
  • ", "
  • Bananes
  • ", "
  • Oranges
  • "]
    Exemple - filter :
    let nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    
    // Nombres pairs
    let pairs = nombres.filter(n => n % 2 === 0);
    console.log(pairs); // Résultat : [2, 4, 6, 8, 10]
    
    // Nombres > 5
    let grands = nombres.filter(n => n > 5);
    console.log(grands); // Résultat : [6, 7, 8, 9, 10]
    
    // Filtrer des objets
    let produits = [
        { nom: "Laptop", prix: 1000, stock: 5 },
        { nom: "Phone", prix: 500, stock: 0 },
        { nom: "Tablet", prix: 300, stock: 10 }
    ];
    
    let enStock = produits.filter(p => p.stock > 0);
    console.log(enStock);
    // Résultat : [{ nom: "Laptop", prix: 1000, stock: 5 }, { nom: "Tablet", prix: 300, stock: 10 }]
    
    let chers = produits.filter(p => p.prix >= 500);
    console.log(chers);
    // Résultat : [{ nom: "Laptop", prix: 1000, stock: 5 }, { nom: "Phone", prix: 500, stock: 0 }]
    Exemple - reduce :
    let nombres = [1, 2, 3, 4, 5];
    
    // Somme
    let somme = nombres.reduce((acc, n) => acc + n, 0);
    console.log(somme); // Résultat : 15
    
    // Produit
    let produit = nombres.reduce((acc, n) => acc * n, 1);
    console.log(produit); // Résultat : 120
    
    // Max
    let max = nombres.reduce((acc, n) => n > acc ? n : acc);
    console.log(max); // Résultat : 5
    
    // Grouper par propriété
    let personnes = [
        { nom: "Ali", categorie: "A" },
        { nom: "Sara", categorie: "B" },
        { nom: "Omar", categorie: "A" }
    ];
    
    let groupes = personnes.reduce((acc, p) => {
        if (!acc[p.categorie]) acc[p.categorie] = [];
        acc[p.categorie].push(p.nom);
        return acc;
    }, {});
    console.log(groupes);
    // Résultat : { A: ["Ali", "Omar"], B: ["Sara"] }
    Exemple - sort :
    // ATTENTION : sort modifie l'original !
    let fruits = ["banane", "pomme", "orange", "kiwi"];
    fruits.sort();
    console.log(fruits); // Résultat : ["banane", "kiwi", "orange", "pomme"]
    
    // Sort avec nombres (PIÈGE)
    let nombres = [10, 2, 100, 5];
    nombres.sort(); // Tri alphabétique !
    console.log(nombres); // Résultat : [10, 100, 2, 5] ❌
    
    // Sort correct avec nombres
    nombres.sort((a, b) => a - b);
    console.log(nombres); // Résultat : [2, 5, 10, 100] ✅
    
    // Tri décroissant
    nombres.sort((a, b) => b - a);
    console.log(nombres); // Résultat : [100, 10, 5, 2]
    
    // Trier des objets
    let users = [
        { nom: "Ali", age: 25 },
        { nom: "Sara", age: 30 },
        { nom: "Omar", age: 20 }
    ];
    users.sort((a, b) => a.age - b.age);
    console.log(users);
    // Résultat : [{ nom: "Omar", age: 20 }, { nom: "Ali", age: 25 }, { nom: "Sara", age: 30 }]

    Méthodes Arrays - Itération

    Méthode Description Retourne
    forEach(callback) Exécute fonction pour chaque élément undefined
    every(callback) Tous les éléments satisfont condition ? Boolean
    some(callback) Au moins un élément satisfait condition ? Boolean
    Exemple - forEach, every, some :
    let nombres = [1, 2, 3, 4, 5];
    
    // forEach - itération simple
    nombres.forEach((n, index) => {
        console.log(`Index ${index}: ${n}`);
    });
    // Résultat : "Index 0: 1", "Index 1: 2", ...
    
    // every - TOUS doivent satisfaire
    let tousPositifs = nombres.every(n => n > 0);
    console.log(tousPositifs); // Résultat : true
    
    let tousGrands = nombres.every(n => n > 3);
    console.log(tousGrands); // Résultat : false
    
    // some - AU MOINS UN doit satisfaire
    let auMoinsUnPair = nombres.some(n => n % 2 === 0);
    console.log(auMoinsUnPair); // Résultat : true
    
    let auMoinsUnGrand = nombres.some(n => n > 10);
    console.log(auMoinsUnGrand); // Résultat : false
    map vs forEach :
    map : retourne nouveau tableau (utilisable en chaîne)
    forEach : ne retourne rien (pour effets de bord)

    Spread Operator (...)

    Usage Exemple
    Copier un tableau [...arr]
    Fusionner tableaux [...arr1, ...arr2]
    Copier un objet {...obj}
    Fusionner objets {...obj1, ...obj2}
    Arguments fonction Math.max(...arr)
    Exemple - Spread operator :
    // Copier un tableau (shallow copy)
    let original = [1, 2, 3];
    let copie = [...original];
    copie.push(4);
    console.log(original); // Résultat : [1, 2, 3]
    console.log(copie);    // Résultat : [1, 2, 3, 4]
    
    // Fusionner des tableaux
    let arr1 = [1, 2];
    let arr2 = [3, 4];
    let fusion = [...arr1, ...arr2];
    console.log(fusion); // Résultat : [1, 2, 3, 4]
    
    // Copier un objet
    let personne = { nom: "Ali", age: 25 };
    let copiePersonne = { ...personne };
    copiePersonne.age = 30;
    console.log(personne.age);      // Résultat : 25 (inchangé)
    console.log(copiePersonne.age); // Résultat : 30
    
    // Fusionner avec override
    let defaults = { theme: "dark", lang: "fr" };
    let userPrefs = { theme: "light" };
    let config = { ...defaults, ...userPrefs };
    console.log(config);
    // Résultat : { theme: "light", lang: "fr" }
    
    // Passer des arguments
    let nombres = [5, 1, 9, 3];
    console.log(Math.max(...nombres)); // Résultat : 9
    console.log(Math.min(...nombres)); // Résultat : 1
    
    // Dans les appels de fonction
    function somme(a, b, c) {
        return a + b + c;
    }
    let nums = [1, 2, 3];
    console.log(somme(...nums)); // Résultat : 6

    Destructuring

    Type Syntaxe Exemple
    Tableau [a, b] = arr const [a, b] = [1, 2]
    Objet {a, b} = obj const {nom, age} = person
    Valeur par défaut {a = 10} const {x = 5} = {}
    Renommage {a: nouveauNom} const {nom: n} = person
    Rest [a, ...reste] const [premier, ...autres] = arr
    Exemple - Destructuring de tableaux :
    // Destructuring simple
    let [a, b] = [1, 2];
    console.log(a); // Résultat : 1
    console.log(b); // Résultat : 2
    
    // Ignorer des éléments
    let [x, , z] = [1, 2, 3];
    console.log(x); // Résultat : 1
    console.log(z); // Résultat : 3
    
    // Valeur par défaut
    let [p, q, r = 3] = [1, 2];
    console.log(r); // Résultat : 3
    
    // Rest elements
    let [premier, ...reste] = [1, 2, 3, 4, 5];
    console.log(premier); // Résultat : 1
    console.log(reste);   // Résultat : [2, 3, 4, 5]
    
    // Échanger des variables
    let m = 1, n = 2;
    [m, n] = [n, m];
    console.log(m); // Résultat : 2
    console.log(n); // Résultat : 1
    Exemple - Destructuring d'objets :
    let personne = { nom: "Ali", age: 25, ville: "Paris" };
    
    // Destructuring simple
    let { nom, age } = personne;
    console.log(nom); // Résultat : "Ali"
    console.log(age); // Résultat : 25
    
    // Renommage
    let { nom: prenom } = personne;
    console.log(prenom); // Résultat : "Ali"
    
    // Valeur par défaut
    let { nom: n, pays = "France" } = personne;
    console.log(n);     // Résultat : "Ali"
    console.log(pays);  // Résultat : "France"
    
    // Rest properties
    let { nom: name, ...autres } = personne;
    console.log(name);   // Résultat : "Ali"
    console.log(autres); // Résultat : { age: 25, ville: "Paris" }
    
    // Destructuring imbriqué
    let user = {
        id: 1,
        info: { nom: "Sara", adresse: { ville: "Lyon" } }
    };
    let { info: { nom: userName, adresse: { ville } } } = user;
    console.log(userName); // Résultat : "Sara"
    console.log(ville);    // Résultat : "Lyon"

    Template Literals

    Syntaxe Description Exemple
    `text ${var}` Interpolation de variables `Bonjour ${nom}`
    `ligne1
    ligne2`
    Chaînes multi-lignes Texte sur plusieurs lignes
    tag`texte` Tagged templates Fonction personnalisée
    Exemple - Template Literals :
    let nom = "Ali";
    let age = 25;
    
    // Interpolation
    let message = `Bonjour, je m'appelle ${nom} et j'ai ${age} ans.`;
    console.log(message);
    // Résultat : "Bonjour, je m'appelle Ali et j'ai 25 ans."
    
    // Expressions
    let a = 5, b = 3;
    console.log(`${a} + ${b} = ${a + b}`);
    // Résultat : "5 + 3 = 8"
    
    // Multi-lignes
    let poeme = `Roses are red,
    Violets are blue,
    JavaScript is awesome,
    And so are you!`;
    console.log(poeme);
    // Résultat : le poème sur plusieurs lignes
    
    // Dans une fonction
    function saluer(nom, heure) {
        return `Bonjour ${nom}, il est ${heure}h`;
    }
    console.log(saluer("Sara", 14));
    // Résultat : "Bonjour Sara, il est 14h"
    
    // Tagged template (avancé)
    function upper(strings, ...values) {
        return strings.reduce((result, str, i) => 
            result + str + (values[i] ? values[i].toUpperCase() : ''), '');
    }
    let resultat = upper`Hello ${nom}!`;
    console.log(resultat); // Résultat : "Hello ALI!"

    Classes

    Élément Syntaxe Description
    Déclaration class Nom {} Définit une classe
    Constructeur constructor() {} Initialise l'instance
    Méthode methode() {} Fonction de la classe
    Héritage class Enfant extends Parent {} Hérite de Parent
    super() super(args) Appelle constructeur parent
    Static static methode() {} Méthode de classe (pas d'instance)
    Getter get prop() {} Accès comme propriété
    Setter set prop(val) {} Modification comme propriété
    Exemple - Classe basique :
    class Personne {
        // Constructeur
        constructor(nom, age) {
            this.nom = nom;
            this.age = age;
        }
        
        // Méthode
        sePresenter() {
            return `Je suis ${this.nom}, j'ai ${this.age} ans`;
        }
        
        // Getter
        get majeur() {
            return this.age >= 18;
        }
        
        // Setter
        set anniversaire(val) {
            this.age = val;
        }
    }
    
    // Instanciation
    let ali = new Personne("Ali", 25);
    console.log(ali.sePresenter()); // Résultat : "Je suis Ali, j'ai 25 ans"
    console.log(ali.majeur);       // Résultat : true
    
    ali.anniversaire = 26;
    console.log(ali.age); // Résultat : 26
    Exemple - Héritage :
    class Animal {
        constructor(nom) {
            this.nom = nom;
        }
        
        parler() {
            return "Son d'animal";
        }
    }
    
    class Chien extends Animal {
        constructor(nom, race) {
            super(nom); // Appel du constructeur parent
            this.race = race;
        }
        
        parler() {
            return `${this.nom} dit: Wouf!`;
        }
    }
    
    class Chat extends Animal {
        parler() {
            return `${this.nom} dit: Miaou!`;
        }
    }
    
    let rex = new Chien("Rex", "Berger");
    let minou = new Chat("Minou");
    
    console.log(rex.parler());  // Résultat : "Rex dit: Wouf!"
    console.log(minou.parler()); // Résultat : "Minou dit: Miaou!"
    console.log(rex.race);      // Résultat : "Berger"
    Exemple - Static :
    class MathUtils {
        static PI = 3.14159;
        
        static carre(x) {
            return x * x;
        }
        
        static cube(x) {
            return x * x * x;
        }
    }
    
    // Utilisation sans instanciation
    console.log(MathUtils.PI);      // Résultat : 3.14159
    console.log(MathUtils.carre(5)); // Résultat : 25
    console.log(MathUtils.cube(3));  // Résultat : 27
    
    // ❌ Ne fonctionne pas sur une instance
    let utils = new MathUtils();
    // console.log(utils.carre(5)); // Erreur

    Modules ES6

    Syntaxe Description
    export const x = 1; Export nommé
    export default fn; Export par défaut
    import { x } from 'module'; Import nommé
    import fn from 'module'; Import par défaut
    import * as mod from 'module'; Import tout
    Exemple - Modules :
    // ===== Fichier: math.js =====
    // Export nommé
    export const PI = 3.14159;
    
    export function addition(a, b) {
        return a + b;
    }
    
    export function multiplication(a, b) {
        return a * b;
    }
    
    // Export par défaut (un seul par fichier)
    export default class Calculatrice {
        constructor() {
            this.resultat = 0;
        }
        add(n) { this.resultat += n; }
    }
    // ===== Fichier: main.js =====
    // Import par défaut
    import Calculatrice from './math.js';
    
    // Import nommé
    import { PI, addition } from './math.js';
    
    // Import avec renommage
    import { multiplication as multi } from './math.js';
    
    // Import tout
    import * as MathModule from './math.js';
    
    // Utilisation
    console.log(PI);                    // Résultat : 3.14159
    console.log(addition(2, 3));        // Résultat : 5
    console.log(MathModule.PI);         // Résultat : 3.14159
    
    let calc = new Calculatrice();
    calc.add(10);
    console.log(calc.resultat); // Résultat : 10

    Promesses (Promises)

    Méthode Description
    new Promise((resolve, reject) => {}) Créer une promesse
    .then(result => {}) Succès
    .catch(error => {}) Erreur
    .finally(() => {}) Toujours exécuté
    Promise.all([p1, p2]) Toutes doivent réussir
    Promise.race([p1, p2]) Première résolue/rejetée
    Promise.allSettled([p1, p2]) Attend toutes les promesses
    Promise.any([p1, p2]) Première résolue
    Exemple - Créer une Promise :
    // Créer une promesse
    const simulerAPI = new Promise((resolve, reject) => {
        const succes = true;
        
        setTimeout(() => {
            if (succes) {
                resolve({ nom: "Ali", age: 25 });
            } else {
                reject("Erreur de connexion");
            }
        }, 1000);
    });
    
    // Utiliser la promesse
    simulerAPI
        .then(data => {
            console.log("Succès:", data);
            return data.nom;
        })
        .then(nom => {
            console.log("Nom:", nom);
        })
        .catch(erreur => {
            console.error("Erreur:", erreur);
        })
        .finally(() => {
            console.log("Terminé");
        });
    
    // Après 1 seconde :
    // Résultat : "Succès: { nom: "Ali", age: 25 }"
    // Résultat : "Nom: Ali"
    // Résultat : "Terminé"
    Exemple - Promise.all :
    let p1 = Promise.resolve(1);
    let p2 = Promise.resolve(2);
    let p3 = new Promise(resolve => setTimeout(() => resolve(3), 1000));
    
    // Promise.all - attend que TOUTES réussissent
    Promise.all([p1, p2, p3])
        .then(resultats => console.log(resultats));
    // Après 1 seconde : Résultat : [1, 2, 3]
    
    // Promise.race - première terminée gagne
    let rapide = Promise.resolve("rapide");
    let lent = new Promise(r => setTimeout(() => r("lent"), 1000));
    
    Promise.race([rapide, lent])
        .then(gagnant => console.log(gagnant));
    // Résultat : "rapide"
    
    // Promise.allSettled - attend toutes (même les échecs)
    let succes = Promise.resolve("OK");
    let echec = Promise.reject("KO");
    
    Promise.allSettled([succes, echec])
        .then(resultats => console.log(resultats));
    // Résultat : [
    //   { status: "fulfilled", value: "OK" },
    //   { status: "rejected", reason: "KO" }
    // ]

    Async / Await

    Syntaxe Description
    async function fn() {} Déclare fonction asynchrone
    await promise Attend résolution de la promesse
    try { await ... } catch(e) {} Gestion d'erreurs avec async/await
    Exemple - Async/Await :
    // Fonction asynchrone
    async function obtenirDonnees() {
        // Simuler un délai
        const delai = ms => new Promise(r => setTimeout(r, ms));
        
        console.log("Début");
        await delai(1000);
        console.log("Après 1 seconde");
        
        return "Terminé";
    }
    
    obtenirDonnees().then(result => console.log(result));
    // Résultat : "Début" (immédiat)
    // Résultat : "Après 1 seconde" (après 1s)
    // Résultat : "Terminé" (après 1s)
    Exemple - Gestion d'erreurs :
    async function fetchUser(id) {
        try {
            const response = await fetch(`/api/users/${id}`);
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}`);
            }
            
            const user = await response.json();
            return user;
        } catch (error) {
            console.error("Erreur:", error.message);
            return null;
        }
    }
    
    // Utilisation
    async function main() {
        const user = await fetchUser(1);
        if (user) {
            console.log("Utilisateur:", user.nom);
        }
    }
    
    // Appels parallèles avec async/await
    async function fetchAll() {
        const [users, posts] = await Promise.all([
            fetch('/api/users').then(r => r.json()),
            fetch('/api/posts').then(r => r.json())
        ]);
        
        console.log(users, posts);
    }
    ⚠️ Async/Await :
    await ne peut être utilisé que dans une fonction async
    Une fonction async retourne toujours une Promise

    DOM Manipulation

    Méthode Retourne
    getElementById('id') 1 élément
    getElementsByClassName('class') HTMLCollection (live)
    getElementsByTagName('tag') HTMLCollection (live)
    querySelector('selector') 1er élément correspondant
    querySelectorAll('selector') NodeList (statique)
    Exemple - Sélection d'éléments :
    // HTML:
    // 
    
    // getElementById - un seul élément
    const header = document.getElementById("header");
    console.log(header.id); // Résultat : "header"
    
    // querySelector - premier élément correspondant
    const premierParagraphe = document.querySelector(".text");
    console.log(premierParagraphe.textContent);
    // Résultat : "Paragraphe 1"
    
    // querySelectorAll - tous les éléments (NodeList)
    const tousLesParagraphes = document.querySelectorAll(".text");
    tousLesParagraphes.forEach(p => console.log(p.textContent));
    // Résultat : "Paragraphe 1", "Paragraphe 2"
    
    // getElementsByClassName - HTMLCollection (live)
    const textes = document.getElementsByClassName("text");
    console.log(textes.length); // Résultat : 2
    
    // Sélecteurs CSS complexes
    const special = document.querySelector("#header > p.text:first-child");
    const inputs = document.querySelectorAll("input[type='text']");

    DOM - Modification

    Propriété/Méthode Description
    element.innerHTML Contenu HTML (interprète les balises)
    element.textContent Texte brut (sécurisé)
    element.setAttribute('attr', 'val') Définit un attribut
    element.getAttribute('attr') Récupère un attribut
    element.classList.add('class') Ajoute une classe
    element.classList.remove('class') Supprime une classe
    element.classList.toggle('class') Ajoute/Supprime une classe
    element.classList.contains('class') Vérifie présence classe
    element.style.property = 'value' Modifie style inline
    Exemple - Modification du DOM :
    const div = document.querySelector("#container");
    
    // innerHTML vs textContent
    div.innerHTML = "Gras";  // Interprète le HTML
    div.textContent = "Gras"; // Affiche tel quel
    
    // Attributs
    div.setAttribute("data-id", "123");
    console.log(div.getAttribute("data-id")); // Résultat : "123"
    
    // Classes
    div.classList.add("active", "visible");
    div.classList.remove("hidden");
    div.classList.toggle("highlight");
    console.log(div.classList.contains("active")); // Résultat : true/false
    
    // Style inline
    div.style.backgroundColor = "blue";
    div.style.fontSize = "20px";
    div.style.display = "none";
    
    // Dataset (data-attributes)
    // 
    console.log(div.dataset.userId); // Résultat : "1" console.log(div.dataset.name); // Résultat : "ali" div.dataset.role = "admin";
    Différence innerHTML vs textContent :
    innerHTML : interprète le HTML (risque XSS)
    textContent : texte brut (plus sûr)

    DOM - Création/Suppression

    Méthode Description
    document.createElement('tag') Crée un élément
    parent.appendChild(child) Ajoute à la fin
    parent.insertBefore(new, ref) Insère avant un élément
    parent.removeChild(child) Supprime un enfant
    element.remove() Supprime l'élément lui-même
    element.replaceChild(new, old) Remplace un enfant
    element.cloneNode(true) Clone l'élément (deep)
    Exemple - Création d'éléments :
    // Créer un élément
    const nouveauParagraphe = document.createElement("p");
    nouveauParagraphe.textContent = "Nouveau paragraphe";
    nouveauParagraphe.classList.add("text");
    
    // Ajouter au DOM
    const container = document.querySelector("#container");
    container.appendChild(nouveauParagraphe);
    
    // Créer plusieurs éléments
    const ul = document.createElement("ul");
    ["Ali", "Sara", "Omar"].forEach(nom => {
        const li = document.createElement("li");
        li.textContent = nom;
        ul.appendChild(li);
    });
    document.body.appendChild(ul);
    
    // Insérer avant un élément
    const reference = document.querySelector("#reference");
    const nouveau = document.createElement("div");
    nouveau.textContent = "Inséré avant";
    reference.parentNode.insertBefore(nouveau, reference);
    
    // insertAdjacentHTML (plus performant)
    container.insertAdjacentHTML("beforeend", "Ajouté");
    // Positions: beforebegin, afterbegin, beforeend, afterend
    
    // Supprimer
    const element = document.querySelector("#toRemove");
    element.remove(); // Moderne
    // element.parentNode.removeChild(element); // Ancien
    
    // Cloner
    const clone = element.cloneNode(true); // true = deep clone

    Événements

    Syntaxe Description
    element.addEventListener('event', fn) Ajoute un écouteur
    element.removeEventListener('event', fn) Supprime un écouteur
    element.onevent = fn Propriété (remplace existant)
    Exemple - Gestionnaires d'événements :
    const btn = document.querySelector("#monBtn");
    
    // addEventListener (recommandé)
    btn.addEventListener("click", function(event) {
        console.log("Bouton cliqué!");
        console.log(event.target); // Élément cliqué
    });
    
    // Arrow function
    btn.addEventListener("click", (e) => {
        console.log("Click avec arrow");
    });
    
    // Fonction nommée (pour pouvoir supprimer)
    function handleClick(e) {
        console.log("Handler nommé");
    }
    btn.addEventListener("click", handleClick);
    btn.removeEventListener("click", handleClick); // Supprime l'écouteur
    
    // Propriété onevent (ancien, écrase les autres)
    btn.onclick = function() {
        console.log("onclick");
    };
    
    // Options avancées
    btn.addEventListener("click", handler, {
        once: true,      // Exécuté une seule fois
        capture: false,  // Phase de capture
        passive: true    // Optimisation scroll
    });

    Événements Courants

    Catégorie Événements
    Souris click, dblclick, mouseenter, mouseleave, mousemove
    Clavier keydown, keyup, keypress (déprécié)
    Formulaire submit, input, change, focus, blur
    Document DOMContentLoaded, load, scroll, resize
    Exemple - Événements courants :
    // Événements souris
    element.addEventListener("click", (e) => {
        console.log(e.clientX, e.clientY); // Position
    });
    
    element.addEventListener("mouseenter", () => {
        element.style.background = "yellow";
    });
    
    element.addEventListener("mouseleave", () => {
        element.style.background = "";
    });
    
    // Événements clavier
    document.addEventListener("keydown", (e) => {
        console.log(e.key);     // "Enter", "a", "Escape"...
        console.log(e.code);    // "Enter", "KeyA", "Escape"...
        console.log(e.ctrlKey); // true si Ctrl enfoncé
        
        if (e.key === "Escape") {
            closeModal();
        }
    });
    
    // Événements formulaire
    const input = document.querySelector("input");
    input.addEventListener("input", (e) => {
        console.log(e.target.value); // Valeur actuelle
    });
    
    input.addEventListener("focus", () => {
        console.log("Focus gagné");
    });
    
    input.addEventListener("blur", () => {
        console.log("Focus perdu");
    });
    
    // Événements document
    document.addEventListener("DOMContentLoaded", () => {
        console.log("DOM prêt");
    });
    
    window.addEventListener("load", () => {
        console.log("Tout chargé (images...)");
    });
    
    window.addEventListener("scroll", () => {
        console.log(window.scrollY);
    });

    Objet Event

    Propriété/Méthode Description
    event.target Élément qui a déclenché l'événement
    event.currentTarget Élément avec l'écouteur
    event.preventDefault() Annule comportement par défaut
    event.stopPropagation() Arrête la propagation
    event.type Type de l'événement
    event.key Touche pressée (keyboard)
    event.clientX / clientY Position souris
    Exemple - Objet Event :
    // target vs currentTarget
    const ul = document.querySelector("ul");
    
    ul.addEventListener("click", (e) => {
        console.log("target:", e.target.tagName);        // LI (cliqué)
        console.log("currentTarget:", e.currentTarget.tagName); // UL
    });
    
    // preventDefault - annuler comportement par défaut
    const form = document.querySelector("form");
    form.addEventListener("submit", (e) => {
        e.preventDefault(); // Empêche l'envoi du formulaire
        console.log("Formulaire non envoyé");
    });
    
    const lien = document.querySelector("a");
    lien.addEventListener("click", (e) => {
        e.preventDefault(); // Empêche la navigation
    });
    
    // stopPropagation - arrêter la propagation
    const parent = document.querySelector("#parent");
    const enfant = document.querySelector("#enfant");
    
    parent.addEventListener("click", () => {
        console.log("Parent cliqué");
    });
    
    enfant.addEventListener("click", (e) => {
        e.stopPropagation(); // Le parent ne recevra pas l'événement
        console.log("Enfant cliqué");
    });
    
    // Informations clavier
    document.addEventListener("keydown", (e) => {
        console.log("Touche:", e.key);
        console.log("Code:", e.code);
        console.log("Modificateurs:", {
            ctrl: e.ctrlKey,
            shift: e.shiftKey,
            alt: e.altKey,
            meta: e.metaKey
        });
    });
    ⚠️ target vs currentTarget :
    target : élément réellement cliqué (peut être un enfant)
    currentTarget : élément sur lequel l'écouteur est attaché

    Local Storage / Session Storage

    Méthode Description
    localStorage.setItem('key', 'value') Stocker une valeur
    localStorage.getItem('key') Récupérer une valeur
    localStorage.removeItem('key') Supprimer une clé
    localStorage.clear() Tout supprimer
    Exemple - Local Storage :
    // Stocker des données simples
    localStorage.setItem("username", "Ali");
    localStorage.setItem("theme", "dark");
    
    // Récupérer
    console.log(localStorage.getItem("username")); // Résultat : "Ali"
    console.log(localStorage.getItem("inexistant")); // Résultat : null
    
    // Supprimer une clé
    localStorage.removeItem("theme");
    
    // Vider tout
    localStorage.clear();
    
    // Stocker des objets (JSON obligatoire)
    const user = { nom: "Ali", age: 25, preferences: { theme: "dark" } };
    localStorage.setItem("user", JSON.stringify(user));
    
    // Récupérer un objet
    const savedUser = JSON.parse(localStorage.getItem("user"));
    console.log(savedUser.nom); // Résultat : "Ali"
    
    // Vérifier si une clé existe
    if (localStorage.getItem("token")) {
        console.log("Utilisateur connecté");
    }
    
    // Nombre d'éléments stockés
    console.log(localStorage.length);
    
    // Parcourir toutes les clés
    for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        console.log(key, localStorage.getItem(key));
    }
    Différence localStorage vs sessionStorage :
    localStorage : persiste après fermeture du navigateur
    sessionStorage : effacé à la fermeture de l'onglet
    Stockage limité aux chaînes de caractères (utiliser JSON.stringify/parse pour objets)

    JSON

    Méthode Description Exemple
    JSON.stringify(obj) Objet → Chaîne JSON JSON.stringify({a: 1}) → '{"a":1}'
    JSON.parse(str) Chaîne JSON → Objet JSON.parse('{"a":1}') → {a: 1}
    Exemple - JSON :
    // Objet JavaScript → JSON
    const personne = {
        nom: "Ali",
        age: 25,
        actif: true,
        hobbies: ["lecture", "sport"]
    };
    
    const jsonStr = JSON.stringify(personne);
    console.log(jsonStr);
    // Résultat : '{"nom":"Ali","age":25,"actif":true,"hobbies":["lecture","sport"]}'
    
    // JSON → Objet JavaScript
    const obj = JSON.parse(jsonStr);
    console.log(obj.nom);   // Résultat : "Ali"
    console.log(obj.age);   // Résultat : 25
    
    // Formatage (indentation)
    const pretty = JSON.stringify(personne, null, 2);
    console.log(pretty);
    // Résultat :
    // {
    //   "nom": "Ali",
    //   "age": 25,
    //   "actif": true,
    //   "hobbies": ["lecture", "sport"]
    // }
    
    // Filtrer les propriétés
    const filtré = JSON.stringify(personne, ["nom", "age"]);
    console.log(filtré); // Résultat : '{"nom":"Ali","age":25}'
    
    // Gestion des erreurs
    try {
        const data = JSON.parse('invalide');
    } catch (e) {
        console.error("JSON invalide:", e.message);
    }
    
    // Valeurs non supportées
    const obj2 = {
        fn: function() {},  // Ignorée
        undef: undefined,   // Ignorée
        date: new Date()    // Convertie en string
    };
    console.log(JSON.stringify(obj2));
    // Résultat : '{"date":"2024-..."}'

    Fetch API

    Syntaxe Description
    fetch('url') Requête GET simple
    fetch('url', options) Requête avec options
    response.json() Parse réponse en JSON
    response.text() Récupère texte brut
    response.ok True si statut 200-299
    response.status Code HTTP (200, 404...)
    Exemple - Fetch GET :
    // GET simple
    fetch('https://jsonplaceholder.typicode.com/users/1')
        .then(response => {
            if (!response.ok) {
                throw new Error('HTTP ' + response.status);
            }
            return response.json();
        })
        .then(user => {
            console.log(user.name);
            console.log(user.email);
        })
        .catch(error => {
            console.error('Erreur:', error);
        });
    
    // GET avec async/await
    async function getUser(id) {
        try {
            const response = await fetch(
                `https://jsonplaceholder.typicode.com/users/${id}`
            );
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}`);
            }
            
            const user = await response.json();
            return user;
        } catch (error) {
            console.error('Erreur:', error);
            return null;
        }
    }
    
    const user = await getUser(1);
    console.log(user.name); // Résultat : "Leanne Graham"
    Exemple - Fetch POST :
    // POST avec données JSON
    async function createUser(userData) {
        const response = await fetch(
            'https://jsonplaceholder.typicode.com/users',
            {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(userData)
            }
        );
        
        const newUser = await response.json();
        return newUser;
    }
    
    const result = await createUser({
        name: 'Ali',
        email: 'ali@email.com'
    });
    console.log(result.id); // Résultat : 11 (nouvel ID)
    
    // PUT (mise à jour complète)
    async function updateUser(id, userData) {
        const response = await fetch(
            `https://jsonplaceholder.typicode.com/users/${id}`,
            {
                method: 'PUT',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(userData)
            }
        );
        return response.json();
    }
    
    // DELETE
    async function deleteUser(id) {
        const response = await fetch(
            `https://jsonplaceholder.typicode.com/users/${id}`,
            { method: 'DELETE' }
        );
        return response.ok; // true si succès
    }

    Gestion d'Erreurs

    Syntaxe Description
    try { ... } catch(e) { ... } Capturer les erreurs
    try { ... } finally { ... } finally toujours exécuté
    throw new Error('message') Lancer une erreur
    error.message Message de l'erreur
    error.name Nom du type d'erreur
    Exemple - try/catch/finally :
    // try/catch simple
    try {
        const resultat = JSON.parse('invalide');
    } catch (error) {
        console.log(error.name);    // Résultat : "SyntaxError"
        console.log(error.message); // Résultat : "Unexpected token..."
    }
    
    // Avec finally
    let connection = null;
    try {
        connection = openConnection();
        // ... opérations
    } catch (error) {
        console.error("Erreur:", error.message);
    } finally {
        if (connection) {
            connection.close(); // Toujours exécuté
        }
    }
    
    // Lancer une erreur personnalisée
    function diviser(a, b) {
        if (b === 0) {
            throw new Error("Division par zéro impossible");
        }
        return a / b;
    }
    
    try {
        diviser(10, 0);
    } catch (e) {
        console.log(e.message); // Résultat : "Division par zéro impossible"
    }
    
    // Types d'erreurs
    throw new TypeError("Mauvais type");
    throw new RangeError("Valeur hors limites");
    throw new ReferenceError("Variable non définie");
    throw new SyntaxError("Syntaxe invalide");
    
    // Erreur personnalisée
    class ValidationError extends Error {
        constructor(message, field) {
            super(message);
            this.name = "ValidationError";
            this.field = field;
        }
    }
    
    throw new ValidationError("Email invalide", "email");

    Expressions Régulières (Regex)

    Syntaxe Description
    /pattern/flags Littéral regex
    new RegExp('pattern', 'flags') Constructeur
    regex.test(str) Teste si correspond → Boolean
    regex.exec(str) Retourne correspondances
    str.match(regex) Retourne correspondances
    str.replace(regex, 'new') Remplace correspondances
    Exemple - Regex :
    // Création
    const regex1 = /hello/i; // Littéral
    const regex2 = new RegExp("hello", "i"); // Constructeur
    
    // test - retourne boolean
    console.log(/hello/.test("hello world")); // Résultat : true
    console.log(/hello/.test("world"));       // Résultat : false
    
    // match - retourne correspondances ou null
    const str = "Mon numéro: 0612345678 et 0698765432";
    const matches = str.match(/\d{10}/g);
    console.log(matches); // Résultat : ["0612345678", "0698765432"]
    
    // exec - une correspondance à la fois
    const regex = /\d+/g;
    let match;
    while ((match = regex.exec("123 abc 456")) !== null) {
        console.log(match[0], "à l'index", match.index);
    }
    // Résultat : "123 à l'index 0", "456 à l'index 8"
    
    // replace
    const texte = "Hello World";
    console.log(texte.replace(/o/g, "0")); // Résultat : "Hell0 W0rld"
    
    // search - index de la première correspondance
    console.log("hello world".search(/world/)); // Résultat : 6
    
    // split avec regex
    console.log("a1b2c3d".split(/\d/)); // Résultat : ["a", "b", "c", "d"]
    Exemple - Patterns courants :
    // Email
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    console.log(emailRegex.test("test@email.com")); // Résultat : true
    
    // Numéro de téléphone français
    const telRegex = /^0[1-9]\d{8}$/;
    console.log(telRegex.test("0612345678")); // Résultat : true
    
    // Code postal
    const cpRegex = /^\d{5}$/;
    console.log(cpRegex.test("75001")); // Résultat : true
    
    // URL
    const urlRegex = /^https?:\/\/[^\s]+$/;
    
    // Mot de passe (min 8 chars, 1 maj, 1 min, 1 chiffre)
    const pwdRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
    console.log(pwdRegex.test("Password1")); // Résultat : true
    
    // Extraire données
    const phrase = "Prix: 50€, Quantité: 3";
    const prix = phrase.match(/\d+/g);
    console.log(prix); // Résultat : ["50", "3"]
    
    // Nettoyer une chaîne
    const dirty = "  Hello   World  ";
    const clean = dirty.replace(/\s+/g, " ").trim();
    console.log(clean); // Résultat : "Hello World"

    Drapeaux Regex

    Drapeau Description
    g Global (toutes les correspondances)
    i Insensible à la casse
    m Multiligne
    s Dotall (. match newline)
    Exemple - Drapeaux :
    // g - global
    console.log("aaa".replace(/a/, "b"));  // Résultat : "baa"
    console.log("aaa".replace(/a/g, "b")); // Résultat : "bbb"
    
    // i - insensible à la casse
    console.log(/hello/i.test("HELLO")); // Résultat : true
    console.log(/hello/.test("HELLO"));  // Résultat : false
    
    // m - multiligne (^ et $ par ligne)
    const texte = "ligne1\nligne2\nligne3";
    console.log(texte.match(/^ligne/g));   // Résultat : ["ligne"]
    console.log(texte.match(/^ligne/gm));  // Résultat : ["ligne", "ligne", "ligne"]
    
    // Combiner les drapeaux
    const regex = /hello/gi;
    console.log("HELLO hello HeLLo".match(regex));
    // Résultat : ["HELLO", "hello", "HeLLo"]

    Set et Map

    Structure Méthodes Description
    Set add(), has(), delete(), clear() Collection de valeurs uniques
    Map set(), get(), has(), delete() Paires clé-valeur, clés quelconques
    Exemple - Set :
    // Créer un Set
    const set = new Set();
    
    // Ajouter des valeurs
    set.add(1);
    set.add(2);
    set.add(2); // Ignoré (déjà présent)
    set.add(3);
    
    console.log(set.size); // Résultat : 3
    console.log(set);      // Résultat : Set {1, 2, 3}
    
    // Vérifier présence
    console.log(set.has(1)); // Résultat : true
    console.log(set.has(5)); // Résultat : false
    
    // Supprimer
    set.delete(2);
    console.log(set.has(2)); // Résultat : false
    
    // Parcourir
    set.forEach(val => console.log(val));
    // Résultat : 1, 3
    
    for (let val of set) {
        console.log(val);
    }
    
    // Cas d'usage : éliminer les doublons
    const nombres = [1, 2, 2, 3, 3, 3, 4];
    const unique = [...new Set(nombres)];
    console.log(unique); // Résultat : [1, 2, 3, 4]
    
    // Vider
    set.clear();
    console.log(set.size); // Résultat : 0
    Exemple - Map :
    // Créer une Map
    const map = new Map();
    
    // Ajouter des paires clé-valeur
    map.set("nom", "Ali");
    map.set("age", 25);
    map.set(1, "un");
    map.set(true, "vrai");
    
    console.log(map.size); // Résultat : 4
    
    // Récupérer une valeur
    console.log(map.get("nom"));  // Résultat : "Ali"
    console.log(map.get("inex")); // Résultat : undefined
    
    // Vérifier présence
    console.log(map.has("age")); // Résultat : true
    
    // Supprimer
    map.delete(1);
    
    // Parcourir
    map.forEach((value, key) => {
        console.log(`${key}: ${value}`);
    });
    
    for (let [key, value] of map) {
        console.log(key, value);
    }
    
    // Obtenir clés et valeurs
    console.log([...map.keys()]);   // Résultat : ["nom", "age", true]
    console.log([...map.values()]); // Résultat : ["Ali", 25, "vrai"]
    
    // Initialiser depuis un tableau
    const map2 = new Map([
        ["a", 1],
        ["b", 2]
    ]);
    console.log(map2.get("a")); // Résultat : 1
    Set vs Array :
    Set : valeurs uniques, pas d'index, pas de doublons
    Map vs Object : Map accepte n'importe quel type comme clé, préserve l'ordre d'insertion

    Méthodes de Tableau Statiques

    Méthode Description
    Array.from(iterable) Crée tableau depuis itérable
    Array.isArray(val) Vérifie si c'est un tableau
    Array.of(elem1, elem2) Crée tableau depuis arguments
    Exemple - Méthodes statiques :
    // Array.from
    console.log(Array.from("hello")); // Résultat : ["h", "e", "l", "l", "o"]
    
    console.log(Array.from([1, 2, 3], x => x * 2));
    // Résultat : [2, 4, 6]
    
    // Depuis un Set
    const set = new Set([1, 2, 2, 3]);
    console.log(Array.from(set)); // Résultat : [1, 2, 3]
    
    // Depuis NodeList
    const divs = document.querySelectorAll("div");
    const divArray = Array.from(divs);
    divArray.forEach(div => console.log(div.id));
    
    // Array.isArray
    console.log(Array.isArray([1, 2, 3]));  // Résultat : true
    console.log(Array.isArray("hello"));    // Résultat : false
    console.log(Array.isArray({ 0: 1, length: 1 })); // Résultat : false
    
    // Array.of
    console.log(Array.of(1, 2, 3));    // Résultat : [1, 2, 3]
    console.log(Array.of("a", "b"));   // Résultat : ["a", "b"]
    console.log(Array(3));             // Résultat : [empty × 3]
    console.log(Array.of(3));          // Résultat : [3]

    Valeurs Falsy et Truthy

    Type Valeurs
    Falsy false, 0, -0, "", null, undefined, NaN, 0n
    Truthy Tout le reste : objets, tableaux, chaînes non vides, nombres non nuls...
    Exemple - Falsy et Truthy :
    // Valeurs FALSY
    const falsy = [false, 0, -0, "", null, undefined, NaN, 0n];
    
    falsy.forEach(val => {
        if (val) {
            console.log("truthy:", val);
        } else {
            console.log("falsy:", val);
        }
    });
    // Résultat : tous "falsy"
    
    // Valeurs TRUTHY (attention aux pièges !)
    const truthy = [
        true,
        1,
        -1,
        "false",      // String non vide
        "0",          // String non vide
        [],           // Tableau vide
        {},           // Objet vide
        function(){}, // Fonction
        Infinity
    ];
    
    truthy.forEach(val => {
        if (val) {
            console.log("truthy:", val);
        }
    });
    // Résultat : tous "truthy"
    
    // Conversion explicite
    console.log(Boolean(0));     // Résultat : false
    console.log(Boolean(""));    // Résultat : false
    console.log(Boolean([]));    // Résultat : true
    console.log(Boolean({}));    // Résultat : true
    
    // Double négation
    console.log(!!0);   // Résultat : false
    console.log(!!"");  // Résultat : false
    console.log(!!1);   // Résultat : true
    console.log(!![]);  // Résultat : true
    ⚠️ Pièges courants :
    [] == false → true (tableau vide est falsy avec ==)
    {} == false → false
    typeof null → "object" (bug historique)

    Conversion de Types

    Conversion Méthode Exemple
    Vers String String(val) ou val.toString() String(123) → "123"
    Vers Number Number(val) ou +val Number("42") → 42
    Vers Integer parseInt(val, base) parseInt("42px") → 42
    Vers Float parseFloat(val) parseFloat("3.14px") → 3.14
    Vers Boolean Boolean(val) ou !!val Boolean(1) → true
    Exemple - Conversion de types :
    // Vers String
    console.log(String(123));        // Résultat : "123"
    console.log(String(true));       // Résultat : "true"
    console.log(String(null));       // Résultat : "null"
    console.log(String(undefined));  // Résultat : "undefined"
    console.log((123).toString());   // Résultat : "123"
    console.log(123 + "");           // Résultat : "123" (concaténation)
    
    // Vers Number
    console.log(Number("42"));     // Résultat : 42
    console.log(Number("42px"));   // Résultat : NaN
    console.log(Number(""));       // Résultat : 0
    console.log(Number(true));     // Résultat : 1
    console.log(Number(false));    // Résultat : 0
    console.log(+"42");            // Résultat : 42 (unaire +)
    
    // parseInt vs Number
    console.log(parseInt("42px"));   // Résultat : 42
    console.log(parseInt("3.14"));   // Résultat : 3
    console.log(parseInt("1010", 2)); // Résultat : 10 (binaire)
    
    // parseFloat
    console.log(parseFloat("3.14"));   // Résultat : 3.14
    console.log(parseFloat("3.14px")); // Résultat : 3.14
    
    // Vers Boolean
    console.log(Boolean(1));      // Résultat : true
    console.log(Boolean(0));      // Résultat : false
    console.log(Boolean(""));     // Résultat : false
    console.log(Boolean("text")); // Résultat : true
    console.log(!!1);             // Résultat : true
    console.log(!!0);             // Résultat : false

    Timeout et Interval

    Méthode Description
    setTimeout(fn, ms) Exécute après délai (retourne ID)
    clearTimeout(id) Annule setTimeout
    setInterval(fn, ms) Exécute régulièrement (retourne ID)
    clearInterval(id) Annule setInterval
    Exemple - setTimeout :
    // setTimeout - exécution différée
    const timeoutId = setTimeout(() => {
        console.log("Exécuté après 2 secondes");
    }, 2000);
    
    // Annuler avant exécution
    clearTimeout(timeoutId);
    
    // Avec paramètres
    setTimeout((nom, age) => {
        console.log(`${nom} a ${age} ans`);
    }, 1000, "Ali", 25);
    // Après 1s : Résultat : "Ali a 25 ans"
    
    // setTimeout immédiat (macro-task)
    setTimeout(() => {
        console.log("Après le code synchrone");
    }, 0);
    console.log("Avant");
    // Résultat : "Avant" puis "Après le code synchrone"
    Exemple - setInterval :
    // setInterval - exécution répétée
    let compteur = 0;
    
    const intervalId = setInterval(() => {
        compteur++;
        console.log("Compteur:", compteur);
        
        if (compteur >= 5) {
            clearInterval(intervalId); // Arrêter après 5
            console.log("Terminé");
        }
    }, 1000);
    
    // Résultat chaque seconde :
    // "Compteur: 1"
    // "Compteur: 2"
    // "Compteur: 3"
    // "Compteur: 4"
    // "Compteur: 5"
    // "Terminé"
    
    // Horloge
    function afficherHeure() {
        const maintenant = new Date();
        console.log(maintenant.toLocaleTimeString());
    }
    const horloge = setInterval(afficherHeure, 1000);
    // clearInterval(horloge); // Pour arrêter

    This en JavaScript

    Contexte Valeur de this
    Fonction simple window (ou undefined en strict mode)
    Méthode d'objet L'objet
    Constructeur (new) La nouvelle instance
    Arrow function Hérite du contexte parent
    call/apply/bind Défini explicitement
    Exemple - this :
    // Fonction simple
    function direThis() {
        console.log(this);
    }
    direThis(); // Résultat : window (ou undefined en strict)
    
    // Méthode d'objet
    const personne = {
        nom: "Ali",
        saluer() {
            console.log("Bonjour, je suis " + this.nom);
        }
    };
    personne.saluer(); // Résultat : "Bonjour, je suis Ali"
    
    // Constructeur
    function Personne(nom) {
        this.nom = nom;
    }
    const p = new Personne("Sara");
    console.log(p.nom); // Résultat : "Sara"
    
    // Arrow function - hérite du parent
    const obj = {
        nom: "Ali",
        methode: () => {
            console.log(this.nom); // this = parent (window)
        }
    };
    obj.methode(); // Résultat : undefined
    
    // Callback perd this
    const obj2 = {
        nom: "Ali",
        noms: ["Sara", "Omar"],
        afficher() {
            this.noms.forEach(function(n) {
                console.log(this.nom + " connait " + n);
                // this.nom = undefined !
            });
        }
    };
    
    // Solution 1: arrow function
    afficher() {
        this.noms.forEach(n => {
            console.log(this.nom + " connait " + n); // OK
        });
    }
    
    // Solution 2: bind
    afficher() {
        this.noms.forEach(function(n) {
            console.log(this.nom + " connait " + n);
        }.bind(this));
    }

    Méthodes call, apply, bind

    Méthode Syntaxe Description
    call() fn.call(thisArg, a, b) Appelle avec this et arguments individuels
    apply() fn.apply(thisArg, [a, b]) Appelle avec this et tableau d'arguments
    bind() const newFn = fn.bind(thisArg) Retourne nouvelle fonction avec this lié
    Exemple - call, apply, bind :
    const personne1 = { nom: "Ali" };
    const personne2 = { nom: "Sara" };
    
    function saluer(salutation, punctuation) {
        console.log(`${salutation}, je suis ${this.nom}${punctuation}`);
    }
    
    // call - arguments individuels
    saluer.call(personne1, "Bonjour", "!");
    // Résultat : "Bonjour, je suis Ali!"
    saluer.call(personne2, "Salut", "?");
    // Résultat : "Salut, je suis Sara?"
    
    // apply - arguments en tableau
    saluer.apply(personne1, ["Hello", "..."]);
    // Résultat : "Hello, je suis Ali..."
    
    // bind - retourne une nouvelle fonction
    const saluerAli = saluer.bind(personne1);
    saluerAli("Coucou", "!");
    // Résultat : "Coucou, je suis Ali!"
    
    // bind avec arguments pré-définis
    const direBonjour = saluer.bind(personne2, "Bonjour");
    direBonjour("!!!");
    // Résultat : "Bonjour, je suis Sara!!!"
    
    // Cas d'usage: emprunter des méthodes
    const arrayLike = { 0: "a", 1: "b", 2: "c", length: 3 };
    const arr = Array.prototype.slice.call(arrayLike);
    console.log(arr); // Résultat : ["a", "b", "c"]
    
    // Math.max avec tableau
    const nombres = [5, 1, 9, 3];
    console.log(Math.max.apply(null, nombres)); // Résultat : 9
    console.log(Math.max(...nombres));          // Résultat : 9 (ES6)

    Optional Chaining & Nullish Coalescing

    Opérateur Description Exemple
    ?. Optional chaining user?.address?.city
    ?? Nullish coalescing value ?? "default"
    ??= Nullish assignment a ??= b (si a null/undefined, a = b)
    ||= OR assignment a ||= b (si a falsy, a = b)
    &&= AND assignment a &&= b (si a truthy, a = b)
    Exemple - Optional Chaining :
    const user = {
        nom: "Ali",
        address: {
            city: "Paris"
        }
    };
    
    // Sans optional chaining
    let ville;
    if (user && user.address && user.address.city) {
        ville = user.address.city;
    }
    
    // Avec optional chaining
    const ville2 = user?.address?.city;
    console.log(ville2); // Résultat : "Paris"
    
    // Si propriété inexistante
    const pays = user?.address?.country;
    console.log(pays); // Résultat : undefined (pas d'erreur!)
    
    // Avec méthodes
    const obj = {
        methode() { return "Hello"; }
    };
    console.log(obj.methode?.()); // Résultat : "Hello"
    console.log(obj.inexistante?.()); // Résultat : undefined (pas d'erreur)
    
    // Avec tableaux
    const arr = [1, 2, 3];
    console.log(arr?.[0]);  // Résultat : 1
    console.log(arr?.[10]); // Résultat : undefined
    
    
    console.log(obj.nom);       // Résultat : "Public"
    console.log(obj[_secret]);  // Résultat : "Privé"
    console.log(Object.keys(obj)); // Résultat : ["nom"] (symbol non listé)
    
    // Symbol.iterator - rendre un objet itérable
    const iterableObj = {
        data: [1, 2, 3],
        [Symbol.iterator]() {
            let index = 0;
            const data = this.data;
            return {
                next() {
                    if (index < data.length) {
                        return { value: data[index++], done: false };
                    }
                    return { done: true };
                }
            };
        }
    };
    
    for (let item of iterableObj) {
        console.log(item); // Résultat : 1, 2, 3
    }
    console.log(obj.methode?.()); // Résultat : "Hello"
    
    // Si la méthode n'existe pas
    console.log(obj.nonExistant?.()); // Résultat : undefined
    
    // Avec les tableaux
    const arr = ["a", "b", "c"];
    console.log(arr?.[0]);   // Résultat : "a"
    console.log(arr?.[100]); // Résultat : undefined
    
    // Supprimer une propriété sûrement
    delete user?.address?.country; // Ne plante pas si country n'existe pas
    Exemple - Nullish Coalescing (??) :
    // ?? retourne la valeur de droite SEULEMENT si gauche est null/undefined
    let a = null;
    let b = undefined;
    let c = 0;
    let d = "";
    let e = false;
    
    console.log(a ?? "défaut"); // Résultat : "défaut"
    console.log(b ?? "défaut"); // Résultat : "défaut"
    console.log(c ?? "défaut"); // Résultat : 0 (0 n'est pas null/undefined)
    console.log(d ?? "défaut"); // Résultat : "" (chaîne vide n'est pas null/undefined)
    console.log(e ?? true);     // Résultat : false
    
    // Comparaison avec || (OR)
    console.log(c || "défaut"); // Résultat : "défaut" (0 est falsy pour ||)
    console.log(d || "défaut"); // Résultat : "défaut" ("" est falsy pour ||)
    
    // Cas d'usage : options par défaut
    function config(options) {
        const timeout = options.timeout ?? 3000; // 0 est une valeur valide
        const retries = options.retries ?? 3;    // 0 est une valeur valide
        return { timeout, retries };
    }
    console.log(config({ timeout: 0 }));
    // Résultat : { timeout: 0, retries: 3 }
    Exemple - Assignations logiques :
    let a = null;
    let b = "valeur";
    
    // ??= (Nullish assignment)
    a ??= "défaut";
    console.log(a); // Résultat : "défaut" (a était null)
    b ??= "autre";
    console.log(b); // Résultat : "valeur" (b n'était pas null/undefined)
    
    // ||= (OR assignment)
    let x = 0;
    x ||= 10;
    console.log(x); // Résultat : 10 (0 est falsy)
    
    let y = 5;
    y ||= 10;
    console.log(y); // Résultat : 5 (5 est truthy)
    
    // &&= (AND assignment)
    let obj = { val: 10 };
    obj.val &&= 20;
    console.log(obj.val); // Résultat : 20 (val était truthy)
    
    obj.val = 0;
    obj.val &&= 30;
    console.log(obj.val); // Résultat : 0 (0 est falsy, pas modifié)
    Optional Chaining :
    obj?.prop : retourne undefined si obj est null/undefined au lieu de lancer une erreur
    arr?.[index] : accès sécurisé aux tableaux
    fn?.() : appel de fonction sécurisé

    Strict Mode

    "use strict"
    Active le mode strict en début de fichier ou fonction :
    • Variables non déclarées → erreur
    • Paramètres dupliqués → erreur
    this vaut undefined (pas window)
    delete sur propriétés non supprimables → erreur
    Exemple - Strict Mode :
    // Sans "use strict"
    maVariable = 10; // Crée une variable globale (mauvais)
    console.log(maVariable); // Résultat : 10
    
    function dupliqueParams(a, a) {
        return a; // Le deuxième écrase le premier
    }
    console.log(dupliqueParams(1, 2)); // Résultat : 2
    
    // Avec "use strict"
    "use strict";
    
    // maVariable = 10; // ❌ ReferenceError: maVariable is not defined
    
    function bonnePratique() {
        "use strict";
        this; // undefined au lieu de window
    }
    
    // function paramsDup(a, a) {} // ❌ SyntaxError
    
    // this global
    function testThis() {
        "use strict";
        console.log(this); // Résultat : undefined
    }
    testThis();
    
    // Pas de delete sur propriétés natives
    // delete Object.prototype; // ❌ TypeError en mode strict

    Navigation dans le DOM (Traversing)

    Propriété Description Retourne
    parentNode Parent direct (peut être n'importe quel noeud) 1 Node
    parentElement Parent direct (uniquement élément HTML) 1 Element
    children Tous les enfants éléments HTMLCollection
    childNodes Tous les enfants (noeuds texte inclus) NodeList
    firstElementChild Premier enfant élément 1 Element
    lastElementChild Dernier enfant élément 1 Element
    nextElementSibling Frère suivant (élément) 1 Element
    previousElementSibling Frère précédent (élément) 1 Element
    Exemple - Traversing DOM :
    // HTML Structure:
    // 
    // Texte avant //

    Paragraphe 1

    // Texte au milieu //

    Paragraphe 2

    // Texte après //
    const parent = document.getElementById("parent"); const premierP = parent.querySelector(".premier"); // Remonter console.log(premierP.parentNode); // Résultat :
    console.log(premierP.parentElement); // Résultat :
    // Descendre console.log(parent.children); // Résultat : HTMLCollection [

    ,

    ] (elements only) console.log(parent.childNodes); // Résultat : NodeList [text,

    , text,

    , text] (inclut les textes) console.log(parent.firstElementChild); // Résultat :

    console.log(parent.lastElementChild); // Résultat :

    // Frères (siblings) const secondP = premierP.nextElementSibling; console.log(secondP); // Résultat :

    console.log(secondP.previousElementSibling); // Résultat :

    // Parcourir tous les enfants éléments for (let child of parent.children) { console.log(child.tagName); } // Résultat : "P", "P"

    ⚠️ Node vs Element :
    childNodes inclut les sauts de ligne et espaces (noeuds texte).
    children ne retourne que les balises HTML. Préférez les propriétés avec "Element".

    Propagation des Événements

    Phase Description Direction
    Capture L'événement descend du document vers la cible Window → Target
    Target L'événement atteint l'élément cliqué Élément précis
    Bubbling L'événement remonte vers les parents Target → Window
    Exemple - Phases de propagation :
    // HTML: 
    const grand = document.getElementById("grand"); const parent = document.getElementById("parent"); const btn = document.getElementById("btn"); // Par défaut : Bubbling (remonte) grand.addEventListener("click", () => console.log("Grand")); parent.addEventListener("click", () => console.log("Parent")); btn.addEventListener("click", () => console.log("Button")); // Clic sur button => "Button", "Parent", "Grand" // Avec Capture (descend) grand.addEventListener("click", () => console.log("Grand Capture"), true); parent.addEventListener("click", () => console.log("Parent Capture"), true); // Clic sur button => "Grand Capture", "Parent Capture", "Button", "Parent", "Grand" // Vérifier la phase btn.addEventListener("click", (e) => { console.log("Phase:", e.eventPhase); // 1 = CAPTURING_PHASE, 2 = AT_TARGET, 3 = BUBBLING_PHASE });
    addEventListener paramètre :
    element.addEventListener('click', fn, false)
    3ème paramètre : false (défaut) = phase Bubbling, true = phase Capture.

    Délégation d'Événements

    Concept clé pour éléments dynamiques :
    Au lieu d'attacher un écouteur sur chaque élément enfant, on l'attache sur le parent.
    ul.addEventListener('click', (e) => { if(e.target.tagName === 'LI') {...} })
    Avantages : Fonctionne pour les éléments ajoutés dynamiquement, économise mémoire.
    Exemple - Délégation :
    // HTML initial
    // 
      //
    • Item 1
    • //
    • Item 2
    • //
    const liste = document.getElementById("liste"); // Délégation : écouteur sur le parent liste.addEventListener("click", (e) => { // Vérifier que c'est bien un LI if (e.target.tagName === "LI") { console.log("Cliqué sur:", e.target.textContent); // Style au clic e.target.classList.toggle("active"); } }); // Ajouter des éléments dynamiquement const nouveauItem = document.createElement("li"); nouveauItem.textContent = "Item 3 (dynamique)"; liste.appendChild(nouveauItem); // Cliquer sur "Item 3" fonctionne aussi ! // Résultat : "Cliqué sur: Item 3 (dynamique)" // Approche SANS délégation (mauvaise pour dynamique) document.querySelectorAll("li").forEach(li => { li.addEventListener("click", () => { // Ne fonctionne PAS pour les nouveaux éléments }); });

    Formulaires et Validation

    Propriété/Méthode Description
    form.elements Collection des champs du formulaire
    input.value Valeur saisie (string)
    input.checked État d'une checkbox/radio
    input.type Type du champ (text, email, password...)
    form.submit() Soumet le formulaire via JS
    form.reset() Réinitialise les champs
    e.preventDefault() Bloque l'envoi classique du formulaire
    Exemple - Gestion de formulaire :
    // 
    // // // // //
    const form = document.getElementById("monForm"); form.addEventListener("submit", (e) => { e.preventDefault(); // Empêche le rechargement // Accès via form.elements const nom = form.elements.nom.value; const email = form.elements.email.value; const accept = form.elements.accept.checked; console.log("Nom:", nom); console.log("Email:", email); console.log("Accepté:", accept); // Validation manuelle if (!nom) { alert("Le nom est requis"); return; } // Validation email simple if (email && !email.includes("@")) { alert("Email invalide"); return; } // Envoi des données (exemple avec FormData) const formData = new FormData(form); console.log("FormData:", Object.fromEntries(formData)); // Résultat : { nom: "Ali", email: "ali@email.com", accept: "on" } // Réinitialiser après envoi form.reset(); });

    Validation HTML5 vs JS

    Validation HTML5 Validation JS
    required, pattern="regex" Logique conditionnelle personnalisée
    type="email", min="5" input.checkValidity()
    Pseudo-classes CSS (:valid, :invalid) Messages d'erreur personnalisés dynamiques
    Exemple - Validation combinée :
    // 
    const password = document.getElementById("password");
    
    // Validation HTML5 avec checkValidity
    password.addEventListener("input", () => {
        if (password.validity.tooShort) {
            password.setCustomValidity(
                `Min 8 caractères (${password.value.length}/8)`
            );
        } else if (!/[A-Z]/.test(password.value)) {
            password.setCustomValidity("Une majuscule requise");
        } else {
            password.setCustomValidity(""); // Tout est OK
        }
        
        password.reportValidity(); // Affiche le message
    });
    
    // Vérifier tout le formulaire
    const form = document.querySelector("form");
    form.addEventListener("submit", (e) => {
        if (!form.checkValidity()) {
            e.preventDefault();
            // Afficher les erreurs
        }
    });
    
    // Pseudo-classes CSS (automatique)
    // input:valid { border-color: green; }
    // input:invalid { border-color: red; }

    AJAX (Asynchronous JavaScript And XML)

    Technologie Caractéristique
    XMLHttpRequest (XHR) Ancien, basé sur callbacks, verbeux
    fetch() Moderne, basé sur Promises, plus simple
    Axios (Librairie) Wrapper HTTP populaire, gère erreurs JSON automatiquement

    Exemple XMLHttpRequest (Classique)

    Étape Code
    1. Création const xhr = new XMLHttpRequest();
    2. Configuration xhr.open('GET', 'url', true);
    3. Callback xhr.onload = function() { ... }
    4. Envoi xhr.send();
    Exemple - XHR complet :
    const xhr = new XMLHttpRequest();
    
    // Configuration
    xhr.open("GET", "https://jsonplaceholder.typicode.com/users/1", true);
    
    // Callback de succès
    xhr.onload = function() {
        if (this.status === 200 && this.readyState === 4) {
            const data = JSON.parse(this.responseText);
            console.log("Utilisateur:", data.name);
        }
    };
    
    // Callback d'erreur
    xhr.onerror = function() {
        console.error("Erreur réseau");
    };
    
    // Suivi du progrès (upload/download)
    xhr.onprogress = function(e) {
        if (e.lengthComputable) {
            console.log(`${e.loaded}/${e.total} bytes`);
        }
    };
    
    // Envoyer la requête
    xhr.send();
    ⚠️ readyState (XHR) :
    0: Non initialisé | 1: Connexion ouverte | 2: Reçue | 3: En cours | 4: Terminée
    On vérifie généralement if (xhr.readyState === 4 && xhr.status === 200)

    Fetch API (Moderne)

    Exemple GET Exemple POST
    fetch('url')
     .then(res => res.json())
     .then(data => console.log(data))
     .catch(err => console.error(err));
    fetch('url', {
     method: 'POST',
     headers: {'Content-Type': 'application/json'},
     body: JSON.stringify(data)
    })
    Exemple - Fetch avancé :
    // Gestion complète des erreurs
    async function apiRequest(url, options = {}) {
        try {
            const response = await fetch(url, {
                headers: {
                    'Content-Type': 'application/json',
                    ...options.headers
                },
                ...options
            });
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            
            const data = await response.json();
            return { success: true, data };
            
        } catch (error) {
            console.error('API Error:', error);
            return { success: false, error: error.message };
        }
    }
    
    // Utilisation
    const result = await apiRequest(
        'https://jsonplaceholder.typicode.com/posts',
        {
            method: 'POST',
            body: JSON.stringify({ title: 'Test', body: 'Contenu', userId: 1 })
        }
    );
    
    if (result.success) {
        console.log("Créé:", result.data);
    }
    
    // Timeout avec AbortController
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 5000);
    
    try {
        const response = await fetch(url, { signal: controller.signal });
        clearTimeout(timeoutId);
        const data = await response.json();
    } catch (e) {
        if (e.name === 'AbortError') {
            console.log("Requête annulée (timeout)");
        }
    }
    Fetch n'échoue pas sur erreur HTTP (404, 500)
    Il faut vérifier manuellement : if (!response.ok) throw new Error(...)
    Fetch n'échoue dans .catch que sur erreurs réseau.

    Async/Await avec Fetch

    Syntaxe recommandée
    async function getData() {
     try {
      const res = await fetch('url');
      if (!res.ok) throw new Error('Erreur HTTP');
      const data = await res.json();
      console.log(data);
     } catch (e) { console.error(e); }
    }
    Exemple - Pattern complet async/await :
    class APIClient {
        constructor(baseUrl) {
            this.baseUrl = baseUrl;
        }
        
        async get(endpoint) {
            const response = await fetch(`${this.baseUrl}${endpoint}`);
            return this._handleResponse(response);
        }
        
        async post(endpoint, data) {
            const response = await fetch(`${this.baseUrl}${endpoint}`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(data)
            });
            return this._handleResponse(response);
        }
        
        async _handleResponse(response) {
            if (!response.ok) {
                const error = await response.text();
                throw new Error(`API Error ${response.status}: ${error}`);
            }
            return response.json();
        }
    }
    
    // Utilisation
    const api = new APIClient('https://jsonplaceholder.typicode.com');
    
    async function loadData() {
        try {
            const users = await api.get('/users');
            console.log(`${users.length} utilisateurs`);
            
            const newUser = await api.post('/users', {
                name: 'Ali',
                email: 'ali@test.com'
            });
            console.log("Créé:", newUser);
            
        } catch (error) {
            console.error("Erreur:", error.message);
        }
    }
    
    loadData();

    JSON (JavaScript Object Notation)

    Règle JSON Exemple
    Clés entre guillemets doubles {"nom": "Ali"} (pas de {nom: "Ali"})
    Pas de virgule finale {"a":1,} ❌ Invalide
    Types autorisés String, Number, Boolean, null, Array, Object
    Types interdits Function, Date, undefined
    Exemple - Gestion JSON :
    // Objet JS vers JSON
    const obj = {
        nom: "Ali",
        age: 25,
        hobbies: ["sport", "musique"],
        actif: true
    };
    
    const json = JSON.stringify(obj);
    console.log(json);
    // Résultat : '{"nom":"Ali","age":25,"hobbies":["sport","musique"],"actif":true}'
    
    // JSON vers Objet JS
    const parsed = JSON.parse(json);
    console.log(parsed.nom); // Résultat : "Ali"
    
    // Sauvegarder dans localStorage
    localStorage.setItem("user", JSON.stringify(obj));
    
    // Récupérer depuis localStorage
    const saved = JSON.parse(localStorage.getItem("user"));
    
    // Deep clone avec JSON
    const clone = JSON.parse(JSON.stringify(obj));
    
    // Gestion des erreurs
    try {
        const data = JSON.parse('{"invalide}');
    } catch (e) {
        console.error("JSON invalide:", e.message);
    }
    
    // Reviver function (transformation lors du parse)
    const data = JSON.parse('{"date":"2024-01-01"}', (key, value) => {
        if (key === "date") return new Date(value);
        return value;
    });
    console.log(data.date.getFullYear()); // Résultat : 2024

    jQuery (Rappel rapide)

    Note : jQuery simplifie la sélection DOM et les effets, mais est moins utilisé dans le JS moderne (ES6+). Utile pour maintenance de vieux projets.
    Sélecteur jQuery JS Équivalent
    Par ID $('#monId') document.getElementById('monId')
    Par Classe $('.maClasse') document.querySelectorAll('.maClasse')
    Par Tag $('div') document.getElementsByTagName('div')
    Exemple - jQuery vs Vanilla JS :
    // JQUERY
    // Sélection
     $('#id').css('color', 'red');
     $('.class').hide();
    
    // Événement
     $('#btn').on('click', function() {
        $(this).toggleClass('active');
    });
    
    // AJAX
     $.ajax({
        url: '/api/data',
        method: 'GET',
        success: function(data) {
            console.log(data);
        }
    });
    
    // VANILLA JS (ES6+)
    // Sélection
    document.getElementById('id').style.color = 'red';
    document.querySelectorAll('.class').forEach(el => el.style.display = 'none');
    
    // Événement
    document.getElementById('btn').addEventListener('click', function() {
        this.classList.toggle('active');
    });
    
    // AJAX
    fetch('/api/data')
        .then(res => res.json())
        .then(data => console.log(data));

    jQuery Méthodes Courantes

    Catégorie Méthode Description
    Contenu .html() / .text() Getter/Setter innerHTML / textContent
    Valeur .val() Getter/Setter value (inputs)
    Attributs .attr('src') Getter/Setter attributs
    CSS .css('color', 'red') Modifier le style
    Classes .addClass() / .removeClass() Gérer les classes
    Events .on('click', fn) Attachement d'événements
    AJAX $.ajax({ url: '...', success: fn }) Requêtes HTTP
    Effets .hide() / .show() / .fadeIn() Animations simples

    Chaînage jQuery

    Syntaxe
    $('.box')
     .css('background', 'blue')
     .fadeIn(500)
     .text('Terminé');
    Exemple - Chaînage jQuery :
    // Chaînage
     $('.card')
        .addClass('active')
        .css({ 'background': 'blue', 'color': 'white' })
        .fadeIn(300)
        .find('.title')
            .text('Nouveau titre')
            .end() // Retourne à .card
        .on('click', function() {
            $(this).toggleClass('expanded');
        });
    
    // Équivalent Vanilla JS (plus verbeux mais standard)
    const cards = document.querySelectorAll('.card');
    cards.forEach(card => {
        card.classList.add('active');
        Object.assign(card.style, { background: 'blue', color: 'white' });
        card.style.display = 'block';
        
        const title = card.querySelector('.title');
        if (title) title.textContent = 'Nouveau titre';
        
        card.addEventListener('click', () => {
            card.classList.toggle('expanded');
        });
    });
    ⚠️ Objet jQuery vs Element DOM :
    $('#id') retourne un objet jQuery.
    document.getElementById('id') retourne un Element DOM.
    On ne peut pas utiliser $('#id').addEventListener ni elementDom.text().
    Conversion jQuery ↔ DOM :
    // jQuery vers DOM
    const $element = $('#monId');
    const domElement = $element[0]; // ou $element.get(0)
    
    // DOM vers jQuery
    const domEl = document.getElementById('monId');
    const $el = $(domEl);
    
    // Exemple pratique
     $('#id')[0].addEventListener('click', handler); // Mixte
    
    // Récupérer la valeur d'un input
    console.log($('input').val());      // jQuery
    console.log($('input')[0].value);   // DOM
    
    // Parcourir une collection jQuery
     $('li').each(function(index) {
        console.log(index, $(this).text());
        // this = élément DOM brut
        // $(this) = objet jQuery
    });

    🗄️ SQL & NoSQL

    Définition : SQL (Structured Query Language) pour bases relationnelles avec schéma fixe vs NoSQL (Not Only SQL) pour bases flexibles sans schéma rigide.

    Commandes SQL Essentielles - DQL (Data Query Language)

    Commande Action Exemple / Options
    SELECT Lire données d'une ou plusieurs tables
    SELECT nom, age FROM users WHERE age > 20;
    SELECT * FROM users;  -- Toutes les colonnes
    SELECT DISTINCT ville FROM users;  -- Valeurs uniques
    WHERE Filtrer les lignes selon condition
    WHERE age > 18 AND ville = 'Paris'
    WHERE nom LIKE 'A%'  -- Commence par A
    WHERE id IN (1, 2, 3)  -- Dans la liste
    ORDER BY Trier les résultats
    ORDER BY age ASC;   -- Croissant (défaut)
    ORDER BY nom DESC;  -- Décroissant
    ORDER BY age DESC, nom ASC;  -- Multi-colonnes
    LIMIT / OFFSET Limiter le nombre de résultats
    LIMIT 10;           -- 10 premiers
    LIMIT 10 OFFSET 20; -- 10 résultats à partir du 21ème
    LIMIT 20, 10;       -- MySQL (offset, limit)
    GROUP BY Regrouper lignes selon colonne(s)
    SELECT ville, COUNT(*) FROM users GROUP BY ville;
    HAVING Filtrer les groupes après regroupement
    SELECT ville, COUNT(*) FROM users 
    GROUP BY ville HAVING COUNT(*) > 5;
    JOIN Combiner données de plusieurs tables
    SELECT u.nom, c.nom 
    FROM users u 
    INNER JOIN commandes c ON u.id = c.user_id;
    ALIAS (AS) Renommer colonnes ou tables
    SELECT nom AS prenom FROM users;
    SELECT u.nom, c.montant FROM users AS u;
    Exemple - Table users utilisée :
    -- Table: users
    -- +----+-------+-----+--------+
    -- | id | nom   | age | ville  |
    -- +----+-------+-----+--------+
    -- |  1 | Ali   |  25 | Paris  |
    -- |  2 | Sara  |  30 | Lyon   |
    -- |  3 | Omar  |  25 | Paris  |
    -- |  4 | Lina  |  22 | Marseille |
    -- +----+-------+-----+--------+
    Exemple - SELECT et WHERE :
    -- Sélectionner nom et age des users de Paris
    SELECT nom, age FROM users WHERE ville = 'Paris';
    
    -- Résultat :
    -- +------+-----+
    -- | nom  | age |
    -- +------+-----+
    -- | Ali  |  25 |
    -- | Omar |  25 |
    -- +------+-----+
    
    -- Valeurs uniques de villes
    SELECT DISTINCT ville FROM users;
    
    -- Résultat :
    -- +-----------+
    -- | ville     |
    -- +-----------+
    -- | Paris     |
    -- | Lyon      |
    -- | Marseille |
    -- +-----------+
    
    -- Filtrage avec LIKE
    SELECT * FROM users WHERE nom LIKE 'A%';
    -- Résultat : La ligne de 'Ali' (commence par A)
    
    -- Opérateurs logiques
    SELECT * FROM users WHERE age >= 25 AND ville = 'Paris';
    -- Résultat : Ali et Omar
    Exemple - ORDER BY et LIMIT :
    -- Trier par age décroissant, puis nom croissant
    SELECT * FROM users ORDER BY age DESC, nom ASC;
    
    -- Résultat :
    -- +----+-------+-----+-----------+
    -- | id | nom   | age | ville     |
    -- +----+-------+-----+-----------+
    -- |  2 | Sara  |  30 | Lyon      |
    -- |  1 | Ali   |  25 | Paris     |
    -- |  3 | Omar  |  25 | Paris     |
    -- |  4 | Lina  |  22 | Marseille |
    -- +----+-------+-----+-----------+
    
    -- Pagination : 2 résultats par page, page 2
    SELECT * FROM users LIMIT 2 OFFSET 2;
    
    -- Résultat (saute les 2 premiers) :
    -- +----+------+-----+-----------+
    -- | id | nom  | age | ville     |
    -- +----+------+-----+-----------+
    -- |  3 | Omar |  25 | Paris     |
    -- |  4 | Lina |  22 | Marseille |
    -- +----+------+-----+-----------+
    Exemple - GROUP BY et HAVING :
    -- Compter les utilisateurs par ville
    SELECT ville, COUNT(*) as nb_users 
    FROM users 
    GROUP BY ville;
    
    -- Résultat :
    -- +-----------+----------+
    -- | ville     | nb_users |
    -- +-----------+----------+
    -- | Paris     |        2 |
    -- | Lyon      |        1 |
    -- | Marseille |        1 |
    -- +-----------+----------+
    
    -- Uniquement les villes avec plus d'1 user
    SELECT ville, COUNT(*) as nb_users 
    FROM users 
    GROUP BY ville 
    HAVING COUNT(*) > 1;
    
    -- Résultat :
    -- +-------+----------+
    -- | ville | nb_users |
    -- +-------+----------+
    -- | Paris |        2 |
    -- +-------+----------+
    
    -- Age moyen par ville
    SELECT ville, AVG(age) as moyenne 
    FROM users 
    GROUP BY ville;
    
    -- Résultat :
    -- Paris a une moyenne de 25.0000

    Commandes SQL - DML (Data Manipulation Language)

    Commande Action Exemple / Syntaxe
    INSERT INTO Ajouter de nouvelles lignes
    INSERT INTO users (nom, age) VALUES ('Marie', 25);
    INSERT INTO users VALUES (1, 'Marie', 25);  -- Toutes colonnes
    INSERT INTO users (nom) VALUES ('A'), ('B'), ('C');  -- Multi-lignes
    UPDATE Modifier des lignes existantes
    UPDATE users SET age = 26 WHERE id = 1;
    UPDATE users SET age = age + 1;  -- Incrémente tout le monde
    UPDATE users SET nom = 'Jane', age = 30 WHERE id = 1;
    DELETE Supprimer des lignes
    DELETE FROM users WHERE id = 1;
    DELETE FROM users;  -- ⚠️ Supprime TOUT (sans WHERE)
    DELETE FROM users WHERE age < 18;
    TRUNCATE Vider complètement une table (rapide)
    TRUNCATE TABLE users;  -- Supprime tout, reset auto-increment
    -- Plus rapide que DELETE sans WHERE, pas de rollback
    Exemple - INSERT :
    -- Insérer un utilisateur
    INSERT INTO users (nom, age, ville) VALUES ('Marie', 28, 'Bordeaux');
    
    -- Vérification
    SELECT * FROM users WHERE nom = 'Marie';
    -- Résultat :
    -- +----+-------+-----+----------+
    -- | id | nom   | age | ville    |
    -- +----+-------+-----+----------+
    -- |  5 | Marie |  28 | Bordeaux |
    -- +----+-------+-----+----------+
    
    -- Insertion multiple
    INSERT INTO users (nom, age) VALUES 
        ('Paul', 35),
        ('Emma', 29);
    
    -- Résultat : 2 nouvelles lignes ajoutées
    Exemple - UPDATE :
    -- Avant
    SELECT nom, age FROM users WHERE id = 1;
    -- Ali, 25
    
    -- Mise à jour
    UPDATE users SET age = 26 WHERE id = 1;
    -- Résultat : Query OK, 1 row affected
    
    -- Après
    SELECT nom, age FROM users WHERE id = 1;
    -- Ali, 26
    
    -- Mise à jour conditionnelle
    UPDATE users SET ville = 'Paris' WHERE age > 30;
    -- Tous les users de plus de 30 ans déménagent à Paris
    
    -- ⚠️ SANS WHERE : modifie TOUTES les lignes
    UPDATE users SET ville = 'Paris';
    -- Résultat : Query OK, 5 rows affected (tous modifiés !)
    Exemple - DELETE vs TRUNCATE :
    -- DELETE avec filtre
    DELETE FROM users WHERE age < 25;
    -- Résultat : Supprime Lina (22 ans)
    
    -- DELETE sans filtre (dangereux)
    DELETE FROM users;
    -- Résultat : Toutes les lignes supprimées
    -- Mais : L'auto-increment garde son compteur
    -- Prochain INSERT aura id = 6 (pas 1)
    
    -- TRUNCATE (réinitialise tout)
    TRUNCATE TABLE users;
    -- Résultat : Table vide + Auto-increment remis à 1
    -- Plus rapide car ne loggue pas chaque ligne
    -- ⚠️ Ne peut pas être annulé (ROLLBACK impossible dans certains SGBD)

    Commandes SQL - DDL (Data Definition Language)

    Commande Action Exemple / Syntaxe
    CREATE TABLE Créer une nouvelle table
    CREATE TABLE users (
      id INT PRIMARY KEY AUTO_INCREMENT,
      nom VARCHAR(50) NOT NULL,
      email VARCHAR(100) UNIQUE,
      age INT DEFAULT 0,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
    ALTER TABLE Modifier structure d'une table
    ALTER TABLE users ADD COLUMN telephone VARCHAR(20);
    ALTER TABLE users DROP COLUMN telephone;
    ALTER TABLE users MODIFY COLUMN age SMALLINT;
    ALTER TABLE users RENAME TO clients;
    DROP TABLE Supprimer une table définitivement
    DROP TABLE users;  -- ⚠️ Supprime structure + données
    DROP TABLE IF EXISTS users;  -- Sans erreur si inexistant
    CREATE INDEX Créer un index pour accélérer recherches
    CREATE INDEX idx_nom ON users(nom);
    CREATE UNIQUE INDEX idx_email ON users(email);
    Exemple - CREATE TABLE :
    -- Création d'une table complète
    CREATE TABLE produits (
        id INT PRIMARY KEY AUTO_INCREMENT,
        nom VARCHAR(100) NOT NULL,
        prix DECIMAL(10, 2) NOT NULL,
        stock INT DEFAULT 0,
        categorie_id INT,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        UNIQUE KEY unique_nom (nom),
        FOREIGN KEY (categorie_id) REFERENCES categories(id)
    );
    
    -- Résultat : Table 'produits' créée
    DESCRIBE produits;
    -- +--------------+---------------+------+-----+-------------------+
    -- | Field        | Type          | Null | Key | Default           |
    -- +--------------+---------------+------+-----+-------------------+
    -- | id           | int           | NO   | PRI | NULL              |
    -- | nom          | varchar(100)  | NO   | UNI | NULL              |
    -- | prix         | decimal(10,2) | NO   |     | NULL              |
    -- | stock        | int           | YES  |     | 0                 |
    -- | categorie_id | int           | YES  | MUL | NULL              |
    -- | created_at   | timestamp     | YES  |     | CURRENT_TIMESTAMP |
    -- +--------------+---------------+------+-----+-------------------+
    Exemple - ALTER TABLE :
    -- Ajouter une colonne
    ALTER TABLE produits ADD COLUMN description TEXT;
    -- Résultat : Nouvelle colonne 'description' ajoutée
    
    -- Modifier une colonne
    ALTER TABLE produits MODIFY COLUMN prix DECIMAL(12, 2);
    -- Résultat : Précision du prix augmentée
    
    -- Renommer une colonne
    ALTER TABLE produits CHANGE COLUMN nom nom_produit VARCHAR(150);
    -- Résultat : Colonne 'nom' renommée en 'nom_produit'
    
    -- Supprimer une colonne
    ALTER TABLE produits DROP COLUMN description;
    -- Résultat : Colonne supprimée (données perdues)
    
    -- Supprimer la table
    DROP TABLE IF EXISTS produits;
    -- Résultat : Table supprimée définitivement

    Contraintes SQL (Constraints)

    Contrainte Description Exemple
    PRIMARY KEY Identifiant unique de la ligne (non null + unique) id INT PRIMARY KEY ou PRIMARY KEY (id, code)
    FOREIGN KEY Lien vers clé primaire d'une autre table
    user_id INT,
    FOREIGN KEY (user_id) REFERENCES users(id)
    ON DELETE CASCADE ON UPDATE CASCADE
    NOT NULL Colonne obligatoire (pas de valeur NULL) nom VARCHAR(50) NOT NULL
    UNIQUE Valeur unique dans la table email VARCHAR(100) UNIQUE
    DEFAULT Valeur par défaut si non spécifiée status VARCHAR(20) DEFAULT 'actif'
    CHECK Vérifie une condition sur la valeur age INT CHECK (age >= 0)
    AUTO_INCREMENT Incrémentation automatique (MySQL) id INT AUTO_INCREMENT PRIMARY KEY
    Exemple - Contraintes en action :
    -- Table avec contraintes
    CREATE TABLE commandes (
        id INT AUTO_INCREMENT PRIMARY KEY,
        user_id INT NOT NULL,
        montant DECIMAL(10,2) NOT NULL CHECK (montant > 0),
        status VARCHAR(20) DEFAULT 'en_cours',
        date_commande DATETIME DEFAULT CURRENT_TIMESTAMP,
        
        -- Clé étrangère
        CONSTRAINT fk_user 
            FOREIGN KEY (user_id) REFERENCES users(id)
            ON DELETE CASCADE  -- Si user supprimé, supprimer commandes
            ON UPDATE CASCADE
    );
    
    -- Test violation NOT NULL
    INSERT INTO commandes (user_id, montant) VALUES (NULL, 100);
    -- Résultat : ERROR 1048: Column 'user_id' cannot be null
    
    -- Test violation CHECK
    INSERT INTO commandes (user_id, montant) VALUES (1, -50);
    -- Résultat : ERROR 3819: Check constraint violated
    
    -- Test DEFAULT
    INSERT INTO commandes (user_id, montant) VALUES (1, 100);
    SELECT * FROM commandes WHERE id = LAST_INSERT_ID();
    -- Résultat : status = 'en_cours' (défaut appliqué)

    Fonctions d'Agrégation (Aggregate Functions)

    Fonction Action Exemple
    COUNT(*) Compte le nombre de lignes SELECT COUNT(*) FROM users; → Nombre total
    COUNT(col) Compte les valeurs non NULL SELECT COUNT(email) FROM users;
    SUM(col) Somme des valeurs SELECT SUM(montant) FROM commandes;
    AVG(col) Moyenne des valeurs SELECT AVG(age) FROM users;
    MIN(col) Valeur minimale SELECT MIN(prix) FROM produits;
    MAX(col) Valeur maximale SELECT MAX(salaire) FROM employes;
    GROUP_CONCAT() Concatène valeurs d'un groupe SELECT GROUP_CONCAT(nom) FROM users; → "A,B,C"
    Exemple - Agrégations :
    -- Table commandes:
    -- +----+---------+--------+
    -- | id | user_id | montant|
    -- +----+---------+--------+
    -- |  1 |       1 |    150 |
    -- |  2 |       1 |     80 |
    -- |  3 |       2 |    200 |
    -- |  4 |       3 |     50 |
    -- +----+---------+--------+
    
    -- COUNT : Nombre total de commandes
    SELECT COUNT(*) as total FROM commandes;
    -- Résultat : 4
    
    -- SUM : Chiffre d'affaires total
    SELECT SUM(montant) as ca_total FROM commandes;
    -- Résultat : 480
    
    -- AVG : Montant moyen
    SELECT AVG(montant) as moyenne FROM commandes;
    -- Résultat : 120.0000
    
    -- MIN / MAX
    SELECT MIN(montant) as min, MAX(montant) as max FROM commandes;
    -- Résultat : min = 50, max = 200
    
    -- Agrégation par groupe
    SELECT user_id, 
           COUNT(*) as nb_commandes, 
           SUM(montant) as total
    FROM commandes 
    GROUP BY user_id;
    
    -- Résultat :
    -- +---------+--------------+-------+
    -- | user_id | nb_commandes | total |
    -- +---------+--------------+-------+
    -- |       1 |            2 |   230 |
    -- |       2 |            1 |   200 |
    -- |       3 |            1 |    50 |
    -- +---------+--------------+-------+

    Jointures (JOIN) - Explications Détaillées

    Type Description Exemple / Schéma
    INNER JOIN Intersection : uniquement les correspondances dans LES DEUX tables
    SELECT u.nom, c.montant 
    FROM users u
    INNER JOIN commandes c ON u.id = c.user_id;
    -- Résultat : uniquement users AVEC commandes
    LEFT JOIN (ou LEFT OUTER JOIN) TOUTES les lignes table gauche + correspondances droite (NULL si pas de correspondance)
    SELECT u.nom, c.montant 
    FROM users u
    LEFT JOIN commandes c ON u.id = c.user_id;
    -- Résultat : tous les users, même sans commande (montant = NULL)
    RIGHT JOIN TOUTES les lignes table droite + correspondances gauche
    -- Inverse de LEFT JOIN
    -- Toutes les commandes même sans user associé
    FULL OUTER JOIN Union : toutes les lignes des deux tables (correspondances + non-correspondances)
    -- MySQL : pas natif, simulé avec UNION
    -- Tous les users ET toutes les commandes
    CROSS JOIN Produit cartésien : toutes les combinaisons possibles
    SELECT * FROM users 
    CROSS JOIN produits;
    -- 100 users × 50 produits = 5000 lignes
    SELF JOIN Jointure d'une table avec elle-même
    -- Employés et leurs managers
    SELECT e.nom, m.nom AS manager
    FROM employes e
    JOIN employes m ON e.manager_id = m.id;
    Exemple - Jeux de données :
    -- Table users:
    -- +----+------+
    -- | id | nom  |
    -- +----+------+
    -- |  1 | Ali  |
    -- |  2 | Sara |
    -- |  3 | Omar |
    -- +----+------+
    
    -- Table commandes:
    -- +----+---------+---------+
    -- | id | user_id | montant |
    -- +----+---------+---------+
    -- |  1 |       1 |     150 |
    -- |  2 |       1 |      80 |
    -- |  3 |       2 |     200 |
    -- +----+---------+---------+
    -- Note : Omar (id=3) n'a aucune commande
    Exemple - INNER JOIN :
    -- INNER JOIN : Intersection uniquement
    SELECT u.nom, c.montant
    FROM users u
    INNER JOIN commandes c ON u.id = c.user_id;
    
    -- Résultat :
    -- +------+---------+
    -- | nom  | montant |
    -- +------+---------+
    -- | Ali  |     150 |
    -- | Ali  |      80 |
    -- | Sara |     200 |
    -- +------+---------+
    -- Omar n'apparaît pas (pas de commande)
    Exemple - LEFT JOIN :
    -- LEFT JOIN : Tous les users, même sans commandes
    SELECT u.nom, c.montant
    FROM users u
    LEFT JOIN commandes c ON u.id = c.user_id;
    
    -- Résultat :
    -- +------+---------+
    -- | nom  | montant |
    -- +------+---------+
    -- | Ali  |     150 |
    -- | Ali  |      80 |
    -- | Sara |     200 |
    -- | Omar |    NULL |  <-- Inclus avec NULL
    -- +------+---------+
    
    -- Trouver les users SANS commandes
    SELECT u.nom
    FROM users u
    LEFT JOIN commandes c ON u.id = c.user_id
    WHERE c.id IS NULL;
    
    -- Résultat : Omar
    Exemple - SELF JOIN :
    -- Table employes:
    -- +----+--------+------------+
    -- | id | nom    | manager_id |
    -- +----+--------+------------+
    -- |  1 | Ali    |       NULL | (Directeur)
    -- |  2 | Sara   |          1 |
    -- |  3 | Omar   |          1 |
    -- |  4 | Lina   |          2 |
    -- +----+--------+------------+
    
    -- Qui est le manager de qui ?
    SELECT e.nom AS employe, m.nom AS manager
    FROM employes e
    LEFT JOIN employes m ON e.manager_id = m.id;
    
    -- Résultat :
    -- +---------+---------+
    -- | employe | manager |
    -- +---------+---------+
    -- | Ali     | NULL    | (Pas de manager)
    -- | Sara    | Ali     |
    -- | Omar    | Ali     |
    -- | Lina    | Sara    |
    -- +---------+---------+

    Opérateurs SQL

    Catégorie Opérateurs Description / Exemple
    Comparaison =, !=, <>, <, >, <=, >= WHERE age >= 18 AND age <= 65
    Logiques AND, OR, NOT WHERE (age > 18 OR pays = 'FR') AND NOT bloque
    Intervalle BETWEEN WHERE age BETWEEN 18 AND 25 (inclus)
    Liste IN, NOT IN WHERE pays IN ('FR', 'BE', 'CH')
    Pattern LIKE, NOT LIKE WHERE nom LIKE 'A%' (% = plusieurs _, _ = 1 caractère)
    NULL IS NULL, IS NOT NULL WHERE email IS NOT NULL (pas = NULL)
    Existence EXISTS, NOT EXISTS WHERE EXISTS (SELECT 1 FROM commandes WHERE user_id = u.id)
    Exemple - Opérateurs :
    -- BETWEEN (inclusif)
    SELECT * FROM users WHERE age BETWEEN 25 AND 30;
    -- Résultat : Ali (25), Sara (30), Omar (25)
    
    -- IN (liste de valeurs)
    SELECT * FROM users WHERE ville IN ('Paris', 'Lyon');
    -- Résultat : Ali, Sara, Omar
    
    -- LIKE (recherche de motifs)
    SELECT * FROM users WHERE nom LIKE '%a%';
    -- Résultat : Ali, Sara, Lina (contiennent 'a')
    
    SELECT * FROM users WHERE nom LIKE '_li';
    -- Résultat : Ali (_ = 1 caractère, puis 'li')
    
    -- NULL (attention !)
    SELECT * FROM users WHERE email = NULL;    -- ❌ Ne fonctionne pas
    SELECT * FROM users WHERE email IS NULL;   -- ✅ Correct
    
    -- EXISTS (sous-requête)
    SELECT nom FROM users u
    WHERE EXISTS (
        SELECT 1 FROM commandes c WHERE c.user_id = u.id
    );
    -- Résultat : Users ayant au moins une commande (Ali, Sara)
    WHERE vs HAVING :
    WHERE : filtre les LIGNES avant regroupement (pas de fonctions d'agrégation)
    HAVING : filtre les GROUPES après regroupement (peut utiliser COUNT, SUM, etc.)

    Ordre d'exécution SQL : FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
    Exemple - WHERE vs HAVING :
    -- SCÉNARIO : Users de Paris ayant passé plus de 1 commande
    
    -- Table users: id, nom, ville
    -- Table commandes: id, user_id, montant
    
    -- ❌ ERREUR : WHERE ne peut pas utiliser COUNT
    SELECT u.nom, COUNT(c.id) as nb
    FROM users u
    JOIN commandes c ON u.id = c.user_id
    WHERE u.ville = 'Paris' AND COUNT(c.id) > 1; -- ERREUR !
    
    -- ✅ CORRECT : WHERE pour filtre ligne, HAVING pour filtre groupe
    SELECT u.nom, COUNT(c.id) as nb
    FROM users u
    JOIN commandes c ON u.id = c.user_id
    WHERE u.ville = 'Paris'        -- 1. Filtre les lignes (Paris seulement)
    GROUP BY u.id, u.nom           -- 2. Groupe par user
    HAVING COUNT(c.id) > 1;        -- 3. Filtre les groupes (plus d'1 commande)
    
    -- Résultat : Ali (2 commandes à Paris)

    SQL vs NoSQL - Comparaison Détaillée

    Critère SQL (Relationnel) NoSQL (Non-relationnel)
    Structure Tables avec lignes et colonnes fixes Documents, clés-valeurs, graphes, colonnes (flexible)
    Schéma Rigide (défini à l'avance, ALTER pour modifier) Dynamique (sans schéma prédéfini, évolutif)
    Langage SQL standardisé API spécifique ou langage de requête propre
    Scalabilité Verticale (serveur plus puissant) Horizontale (ajout de serveurs, sharding)
    Jointures Natives et optimisées (JOIN) Non supportées ou dénormalisées (embarquées)
    Transactions ACID complètes (Atomicité, Cohérence, Isolation, Durabilité) BASE (Basically Available, Soft state, Eventually consistent)
    Cas d'usage Données structurées, transactions complexes, reporting Big Data, temps réel, contenu varié, haute disponibilité
    Exemples MySQL, PostgreSQL, Oracle, SQL Server MongoDB, Redis, Cassandra, Neo4j, Couchbase

    Types NoSQL - Détail par Catégorie

    Type Structure Exemple Usage / Cas d'usage
    Document Documents JSON/BSON (clé-valeur hiérarchique) MongoDB, CouchDB Contenu web, catalogues produits, CMS, données semi-structurées
    Key-Value Clé unique → Valeur (string, blob, etc.) Redis, DynamoDB, Riak Cache, sessions utilisateur, paniers e-commerce, temps réel
    Column Family Colonnes groupées en familles (sparse) Cassandra, HBase Big Data, logs, time-series, analytics massive
    Graph Nœuds + Relations + Propriétés Neo4j, Amazon Neptune Réseaux sociaux, recommandations, fraude, knowledge graphs
    Exemple - SQL vs NoSQL (Structure) :
    -- SQL (Table 'users')
    -- +----+------+-----+--------+
    -- | id | nom  | age | ville  |
    -- +----+------+-----+--------+
    -- |  1 | Ali  |  25 | Paris  |
    -- +----+------+-----+--------+
    
    -- NoSQL - Document (MongoDB)
    {
        "_id": ObjectId("507f1f77bcf86cd799439011"),
        "nom": "Ali",
        "age": 25,
        "adresse": {
            "ville": "Paris",
            "cp": "75001"
        },
        "hobbies": ["sport", "musique"],  // Données imbriquées
        "actif": true
    }
    
    -- NoSQL - Key-Value (Redis)
    SET user:1 '{"nom":"Ali","age":25}'
    GET user:1
    -- Résultat : '{"nom":"Ali","age":25}'
    
    -- NoSQL - Graph (Neo4j - Cypher)
    CREATE (u:User {nom: 'Ali'})-[:LIVES_IN]->(c:City {nom: 'Paris'});

    Exemples de Requêtes NoSQL (MongoDB)

    Opération SQL MongoDB (NoSQL)
    Lire tous SELECT * FROM users; db.users.find();
    Lire avec filtre SELECT * FROM users WHERE age > 20; db.users.find({ age: { $gt: 20 } });
    Lire colonnes spécifiques SELECT nom, age FROM users; db.users.find({}, { nom: 1, age: 1, _id: 0 });
    Insérer INSERT INTO users (nom, age) VALUES ('Marie', 25); db.users.insertOne({ nom: 'Marie', age: 25 });
    Mettre à jour UPDATE users SET age = 26 WHERE id = 1; db.users.updateOne({ _id: 1 }, { $set: { age: 26 } });
    Supprimer DELETE FROM users WHERE id = 1; db.users.deleteOne({ _id: 1 });
    Compter SELECT COUNT(*) FROM users; db.users.countDocuments();
    Exemple - Requêtes MongoDB avec résultats :
    // Collection users (équivalent table SQL)
    // { "_id": 1, "nom": "Ali", "age": 25, "ville": "Paris" }
    // { "_id": 2, "nom": "Sara", "age": 30, "ville": "Lyon" }
    
    // Trouver tous les users
    db.users.find();
    // Résultat : Tous les documents
    
    // Filtrer (age > 25)
    db.users.find({ age: { $gt: 25 } });
    // Résultat : { "_id": 2, "nom": "Sara", "age": 30, ... }
    
    // Opérateurs MongoDB :
    // $gt (>) , $gte (>=), $lt (<), $lte (<=), $ne (!=)
    // $in (IN), $nin (NOT IN), $exists (existe)
    
    // Combinaison de conditions
    db.users.find({
        age: { $gte: 20, $lte: 30 },
        ville: { $in: ["Paris", "Lyon"] }
    });
    // Résultat : Ali et Sara
    
    // Projection (colonnes spécifiques)
    db.users.find({}, { nom: 1, age: 1, _id: 0 });
    // Résultat : { "nom": "Ali", "age": 25 }, { "nom": "Sara", "age": 30 }
    
    // Insertion
    db.users.insertOne({ 
        nom: "Omar", 
        age: 22, 
        hobbies: ["lecture", "sport"] 
    });
    // Résultat : { "acknowledged": true, "insertedId": ObjectId("...") }
    
    // Update
    db.users.updateOne(
        { nom: "Ali" },           // Filtre
        { $set: { age: 26 } }     // Modification
    );
    // Résultat : { "acknowledged": true, "modifiedCount": 1 }
    
    // Delete
    db.users.deleteOne({ nom: "Omar" });
    // Résultat : { "acknowledged": true, "deletedCount": 1 }

    Normalisation vs Dénormalisation

    Concept Description Avantage / Inconvénient
    Normalisation (SQL) Organiser données pour éviter redondance (3NF, BCNF) ✓ Intégrité, pas de duplication ✗ Jointures complexes
    Dénormalisation (NoSQL) Dupliquer données pour lecture rapide ✓ Lecture rapide, pas de jointure ✗ Duplication, mise à jour complexe
    Exemple - Normalisation (SQL) :
    -- 1NF : Éliminer les groupes répétitifs
    -- Table users: id, nom, adresse_id
    -- Table adresses: id, rue, ville, cp
    
    -- 2NF : Éliminer les dépendances partielles
    -- Table commandes: id, user_id, date, montant
    -- (Clé primaire = id, toutes les colonnes dépendent de id)
    
    -- 3NF : Éliminer les dépendances transitives
    -- Table users: id, nom, departement_id
    -- Table departements: id, nom_departement, region
    -- (La région dépend du département, pas du user)
    
    -- Avantage : Pas de redondance
    -- Si le département change de région, une seule mise à jour
    
    -- Inconvénient : Requêtes avec jointures
    SELECT u.nom, d.nom_departement, d.region
    FROM users u
    JOIN departements d ON u.departement_id = d.id;
    Exemple - Dénormalisation (NoSQL) :
    // Document MongoDB avec données embarquées
    {
        "_id": 1,
        "nom": "Ali",
        "departement": {
            "id": 75,
            "nom": "Paris",
            "region": "Île-de-France"
        },
        "commandes": [
            { "id": 101, "montant": 150, "date": "2024-01-15" },
            { "id": 102, "montant": 80, "date": "2024-02-01" }
        ]
    }
    
    // Avantage : Lecture en une seule requête
    db.users.find({ nom: "Ali" });
    // Toutes les données sont là, pas de jointure
    
    // Inconvénient : Mise à jour complexe
    // Si la région change, il faut mettre à jour TOUS les users de Paris
    db.users.updateMany(
        { "departement.id": 75 },
        { $set: { "departement.region": "Nouvelle Region" } }
    );
    
    // Duplication : Les données du département sont répétées pour chaque user

    Indexation SQL

    Type d'Index Description Exemple
    PRIMARY KEY Index unique + clusterisé (ordre physique) Recherche rapide par ID
    UNIQUE Index garantissant unicité Email unique, code produit
    INDEX (B-Tree) Index standard pour recherches rapides CREATE INDEX idx_nom ON users(nom);
    FULLTEXT Index pour recherche texte Recherche dans articles, descriptions
    COMPOSITE Index sur plusieurs colonnes CREATE INDEX idx_nom_prenom ON users(nom, prenom);
    Exemple - Indexation :
    -- Créer un index simple
    CREATE INDEX idx_ville ON users(ville);
    
    -- Requête optimisée
    SELECT * FROM users WHERE ville = 'Paris';
    -- Sans index : Full table scan (lent)
    -- Avec index : Recherche directe (rapide)
    
    -- Index composite (ordre important !)
    CREATE INDEX idx_ville_age ON users(ville, age);
    
    -- Requêtes qui utilisent l'index composite :
    SELECT * FROM users WHERE ville = 'Paris';            -- ✅ Utilise index
    SELECT * FROM users WHERE ville = 'Paris' AND age > 25; -- ✅ Utilise index
    SELECT * FROM users WHERE age > 25;                   -- ❌ N'utilise PAS l'index
    
    -- Index unique
    CREATE UNIQUE INDEX idx_email ON users(email);
    -- Empêche les doublons d'email + accélère la recherche
    
    -- Index FULLTEXT
    CREATE FULLTEXT INDEX idx_contenu ON articles(contenu);
    SELECT * FROM articles WHERE MATCH(contenu) AGAINST('javascript');
    
    -- Voir les index
    SHOW INDEX FROM users;
    
    -- Supprimer un index
    DROP INDEX idx_ville ON users;
    EXPLAIN - Analyser les performances :
    -- Avant l'index
    EXPLAIN SELECT * FROM users WHERE ville = 'Paris';
    -- +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    -- | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
    -- +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    -- |  1 | SIMPLE      | users | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 1000 |    10.00 | Using where |
    -- +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    -- type = ALL : Full table scan (lent)
    
    -- Après CREATE INDEX idx_ville ON users(ville);
    EXPLAIN SELECT * FROM users WHERE ville = 'Paris';
    -- type = ref : Utilise l'index (rapide)
    -- key = idx_ville : Index utilisé
    ⚠️ Points clés pour QCM :
    1. Ordre SQL : SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT
    2. WHERE vs HAVING : WHERE filtre les lignes (avant), HAVING filtre les groupes (après)
    3. Jointures : INNER = intersection, LEFT = tout gauche + correspondances, RIGHT = tout droite + correspondances
    4. NULL : Jamais = NULL, toujours IS NULL ou IS NOT NULL
    5. SQL vs NoSQL : SQL = ACID, schéma rigide, vertical | NoSQL = BASE, schéma flexible, horizontal
    6. DELETE vs TRUNCATE : DELETE = ligne par ligne (rollback possible), TRUNCATE = tout d'un coup (rapide, reset auto-increment)
    Exemple - Ordre d'exécution complet :
    -- Requête complète
    SELECT u.ville, COUNT(*) as nb, AVG(u.age) as moyenne
    FROM users u
    WHERE u.age >= 18
    GROUP BY u.ville
    HAVING COUNT(*) > 1
    ORDER BY moyenne DESC
    LIMIT 2;
    
    -- ORDRE D'EXÉCUTION :
    -- 1. FROM users u         → Charge la table
    -- 2. WHERE u.age >= 18    → Filtre les lignes (exclut les mineurs)
    -- 3. GROUP BY u.ville     → Regroupe par ville
    -- 4. HAVING COUNT(*) > 1  → Filtre les groupes (villes avec 1 seul user exclus)
    -- 5. SELECT ...           → Calcule COUNT et AVG pour chaque groupe
    -- 6. ORDER BY moyenne DESC → Trie par moyenne décroissante
    -- 7. LIMIT 2              → Garde les 2 premiers résultats
    
    -- Résultat final :
    -- +--------+----+---------+
    -- | ville  | nb | moyenne |
    -- +--------+----+---------+
    -- | Paris  |  2 |   25.00 |
    -- | Lyon   |  2 |   28.00 |
    -- +--------+----+---------+

    🏗️ OOP & UML

    Définitions OOP

    Programmation Orientée Objet (POO/OOP) : Paradigme de programmation basé sur le concept d'"objets" qui contiennent des données (attributs) et du code (méthodes). Un objet est une instance d'une classe.

    Classe vs Objet

    Concept Définition Exemple
    Classe Modèle/blueprint qui définit les attributs et méthodes communs à un ensemble d'objets Classe Voiture (définit couleur, marque, démarrer())
    Objet Instance concrète d'une classe créée en mémoire Objet maVoiture (rouge, Toyota, instance spécifique)
    Exemple - Classe et Instanciation :
    // ===== DÉFINITION DE LA CLASSE (Le Plan/Blueprint) =====
    class Voiture {
        // Attributs (Données)
        String marque;
        String couleur;
    
        // Méthode (Comportement)
        void demarrer() {
            System.out.println("Vroom ! La " + this.marque + " démarre.");
        }
    }
    
    // ===== CRÉATION D'OBJETS (Instanciation) =====
    public class Main {
        public static void main(String[] args) {
            // Création de l'objet 1 (Instance)
            Voiture maVoiture = new Voiture(); 
            maVoiture.marque = "Toyota";    // Assignation d'attribut
            maVoiture.couleur = "Rouge";
            
            // Création de l'objet 2 (Autre Instance)
            Voiture taVoiture = new Voiture();
            taVoiture.marque = "Peugeot";
            
            // Utilisation
            maVoiture.demarrer(); // Appel de méthode sur l'objet 1
        }
    }
    
    // Résultat :
    // Vroom ! La Toyota démarre.

    Piliers OOP

    Concept Définition
    Encapsulation Contrôle d'accès (private/public/protected) - cacher les données et l'implémentation interne, exposer uniquement l'interface nécessaire
    Héritage Classe enfant hérite de classe parent (extends) - relation "est un". Permet la réutilisation du code et la création de hiérarchies
    Polymorphisme Même méthode, comportements différents selon l'objet. Permet de traiter des objets de classes différentes de manière uniforme
    Abstraction Modèles génériques (abstract/interface) - cacher la complexité et ne montrer que l'essentiel. Définit un contrat sans implémentation

    Modificateurs d'Accès (Encapsulation)

    Modificateur Accès Symbole UML
    private Uniquement dans la classe - (moins)
    public Partout (défaut) + (plus)
    protected Classe + sous-classes # (dièse)
    package Même package uniquement ~ (tilde)
    Exemple - Encapsulation (Getters/Setters) :
    class CompteBancaire {
        // ATTRIBUT PRIVÉ : Non accessible directement de l'extérieur
        // Mot clé : -solde (UML) / private (Code)
        private double solde = 0; 
    
        // MÉTHODE PUBLIQUE : Interface pour lire (Getter)
        // Mot clé : +getSolde (UML) / public (Code)
        public double getSolde() {
            return this.solde;
        }
    
        // MÉTHODE PUBLIQUE : Interface pour modifier de façon contrôlée (Setter)
        public void deposer(double montant) {
            if (montant > 0) {
                this.solde += montant; // Modification interne autorisée
            }
        }
    }
    
    // Utilisation
    CompteBancaire compte = new CompteBancaire();
    // compte.solde = 1000;  // ❌ ERREUR : solde has private access
    compte.deposer(1000);    // ✅ OK : Passe par l'interface publique
    System.out.println("Solde : " + compte.getSolde());
    
    // Résultat :
    // Solde : 1000.0

    Héritage (extends)

    Syntaxe : class Enfant extends Parent

    Caractéristiques :
    • La classe enfant hérite de tous les attributs et méthodes public et protected du parent
    • Relation "est un" : un Chien EST UN Animal
    • Permet d'éviter la duplication de code
    • Une classe peut hériter d'une seule classe (héritage simple)
    Exemple - Héritage :
    // ===== CLASSE PARENT (Généralisation) =====
    class Animal {
        String nom;
    
        void manger() {
            System.out.println(this.nom + " mange.");
        }
    }
    
    // ===== CLASSE ENFANT (Spécialisation) =====
    // Mot clé : extends (Héritage)
    class Chien extends Animal {
        // Hérite de 'nom' et 'manger()' automatiquement
        
        void aboyer() {
            System.out.println("Wouf !");
        }
    }
    
    // Utilisation
    Chien rex = new Chien();
    rex.nom = "Rex";    // Attribut hérité de Animal
    rex.manger();       // Méthode héritée de Animal
    rex.aboyer();       // Méthode propre à Chien
    
    // Résultat :
    // Rex mange.
    // Wouf !

    Polymorphisme

    Types de polymorphisme :
    • Polymorphisme d'héritage : Redéfinition de méthodes (@Override) - comportement spécifique dans chaque classe fille
    • Polymorphisme d'interface : Une classe implémente plusieurs interfaces
    • Surcharge (overloading) : Même nom de méthode, paramètres différents
    Exemple - Polymorphisme (Redéfinition/Override) :
    class Animal {
        void faireSon() {
            System.out.println("L'animal fait un bruit.");
        }
    }
    
    class Chat extends Animal {
        // REDÉFINITION : Même signature, comportement différent
        @Override
        void faireSon() {
            System.out.println("Miaou !");
        }
    }
    
    class Chien extends Animal {
        @Override
        void faireSon() {
            System.out.println("Wouf !");
        }
    }
    
    // ===== UTILISATION POLYMORPHE =====
    // Le type déclaré est Animal, mais l'objet réel change
    Animal monAnimal;
    
    monAnimal = new Chat();
    monAnimal.faireSon(); // Appelle la méthode de Chat
    
    monAnimal = new Chien();
    monAnimal.faireSon(); // Appelle la méthode de Chien
    
    // Résultat :
    // Miaou !
    // Wouf !

    Abstraction (abstract / interface)

    Concept Définition Usage
    Classe Abstraite Classe qui ne peut pas être instanciée, peut contenir méthodes abstraites (sans corps) et concrètes Factoriser le code commun entre classes liées
    Interface Contrat qui définit uniquement des signatures de méthodes (sans implémentation) Définir un comportement que plusieurs classes peuvent implémenter
    Exemple - Interface et Implémentation :
    // ===== INTERFACE (Le Contrat) =====
    // Mot clé : interface
    interface PeutVoler {
        void voler(); // Signature uniquement, pas de corps
    }
    
    // ===== IMPLÉMENTATION =====
    // Mot clé : implements
    class Oiseau implements PeutVoler {
        @Override
        public void voler() {
            System.out.println("L'oiseau vole dans le ciel.");
        }
    }
    
    class Avion implements PeutVoler {
        @Override
        public void voler() {
            System.out.println("L'avion vole avec des moteurs.");
        }
    }
    
    // Utilisation
    PeutVoler objetVolant1 = new Oiseau();
    PeutVoler objetVolant2 = new Avion();
    
    objetVolant1.voler();
    objetVolant2.voler();
    
    // Résultat :
    // L'oiseau vole dans le ciel.
    // L'avion vole avec des moteurs.

    Diagramme de Classes UML

    Structure d'une classe UML :
    ┌─────────────────┐
    │   NomClasse     │  ← Nom de la classe
    ├─────────────────┤
    │ - attributPrivé  │  ← Attributs (visibilité nom : type)
    │ # protégé       │
    │ + public        │
    ├─────────────────┤
    │ + méthode()     │  ← Méthodes (visibilité nom() : typeRetour)
    │ - privée()      │
    └─────────────────┘
                

    Relations UML - Diagramme de Classes

    Relation Symbole Signification Exemple
    Héritage (Généralisation) ◄──── (flèche vide) extends - "est un" Chien ◄── Animal
    Agrégation ◇──── (losange vide) "a un" (faible) - partie vit indépendamment Université ◇── Etudiant
    Composition ◆──── (losange plein) "est composé de" (fort) - partie meurt avec le tout Maison ◆── Piece
    Association ────── Utilise / connaît - relation structurelle Professeur ──── Cours
    Dépendance - - - -> (flèche pointillée) Utilise temporairement Client - - -> Commande
    Exemple - Composition vs Agrégation :
    // ===== COMPOSITION (Forte) =====
    // Maison & Piece : Si Maison détruite → Pièces détruites
    class Maison {
        private Piece[] pieces; // Les pièces sont créées DANS la maison
        
        Maison() {
            this.pieces = new Piece[]{new Piece(), new Piece()}; 
        }
    }
    
    // ===== AGRÉGATION (Faible) =====
    // Université & Etudiant : Si Université fermée → Etudiants existent toujours
    class Universite {
        private List<Etudiant> etudiants; 
        
        void ajouterEtudiant(Etudiant e) {
            this.etudiants.add(e); // L'étudiant est créé AILLEURS et passé en paramètre
        }
    }
    
    // Mots clés Code :
    // Composition : Création interne (new dans constructeur)
    // Agrégation : Passage externe (setters ou constructeur)
    Différence Agrégation vs Composition :
    Agrégation (◇) : partie indépendante (Etudiant existe sans Université)
    Composition (◆) : partie dépendante (Piece détruite avec Maison)

    Diagramme Use Case (Cas d'Utilisation)

    Éléments :
    • Acteur : Entité externe qui interagit avec le système (représenté par un bonhomme stickman)
    • Use Case : Fonctionnalité du système (ovale avec nom de l'action)
    • Système : Frontière du système (rectangle)
    Relation Symbole Signification
    <<include>> ───▶ (flèche pointillée) Toujours exécuté - le cas de base inclut obligatoirement l'autre
    <<extend>> ◀─── (flèche pointillée inverse) Conditionnel (optionnel) - extension sous certaines conditions
    Exemple - Relation Include vs Extend :
    USE CASE : "Retirer de l'argent"
    ├── <<include>> : "Vérifier Solde"
    │   → Obligatoire. Pour retirer, on DOIT vérifier le solde.
    │
    └── <<extend>> : "Imprimer Reçu"
        → Optionnel. On peut retirer sans imprimer, seulement si le client le demande.
    
    // Mots Clés UML :
    // Include = "Tout le temps" (Dépendance obligatoire)
    // Extend = "Parfois" (Optionnel sous condition)

    Diagramme de Séquence

    Définition : Représentation graphique qui décrit l'interaction des objets dans un système au fil du temps. Montre la séquence des messages échangés entre les objets.

    Éléments clés :
    • Lignes de vie : Lignes verticales représentant les objets participants
    • Messages : Flèches horizontales entre lignes de vie (synchrone ▬▶, asynchrone -▶)
    • Barres d'activation : Rectangles sur la ligne de vie indiquant quand l'objet est actif
    • Message de retour : Flèche pointillée (--▶) indiquant la réponse
    Usage : Modéliser un scénario d'utilisation spécifique, montrer l'ordre chronologique des interactions.
    Exemple - Séquence "Authentification" :
    // Représentation textuelle d'un diagramme de séquence
    
    Acteur: Utilisateur
    Objet1: InterfaceWeb
    Objet2: AuthService
    Objet3: BaseDeDonnees
    
    Séquence :
    1. Utilisateur --► InterfaceWeb : saisirLogin(mdp)
    2. InterfaceWeb --► AuthService : verifier(identifiants)
    3. AuthService --► BaseDeDonnees : trouverUtilisateur(email)
    4. BaseDeDonnees --► AuthService : retourner(Utilisateur)
    5. AuthService --► InterfaceWeb : retourner(Token) // Message retour pointillé
    6. InterfaceWeb --► Utilisateur : afficherAccueil()
    
    // Mots clés Séquence :
    // Chronologie : Lecture de haut en bas.
    // Activation : Barre verticale sur l'objet pendant qu'il traite la demande.

    Diagramme d'États-Transitions (State Machine)

    Définition : Visualise les états d'un automate à états finis. Représente le cycle de vie complet d'un objet ou système en montrant les états possibles et les transitions entre ces états.

    Éléments :
    • État : Rectangle aux coins arrondis (condition/moment dans la vie d'un objet)
    • État initial : Cercle plein (début)
    • État final : Cercle avec bordure (fin)
    • Transition : Flèche entre états, étiquetée par événement [garde] / action
    Syntaxe transition : événement [condition] / activité
    Exemple : appuyerBouton [lumièreAllumée==false] / allumerLumière
    Exemple - Cycle de vie "Commande" :
    États possibles : [EnCours], [Payée], [Expédiée], [Annulée]
    
    (●) --créer--> [EnCours]
      |
      |-- payer --> [Payée]
      |    |
      |    |-- expédier --> [Expédiée] -- livrer --> (◉) Final
      |
      |-- annuler --> [Annulée] -- rembourser --> (◉) Final
    
    // Exemple de Transition avec Garde :
    // Événement : payer
    // Garde : [montant > 0]  (Condition à vérifier)
    // Action : / envoyerEmailConfirmation
    
    // Mots clés États :
    // État : Situation stable (objet "attend").
    // Transition : Passage instantané déclenché par un événement.

    💻 C / Java / Python / PHP

    C - Points Clés

    C : Langage de programmation procédural de bas niveau, compilé, utilisé pour la programmation système et embarquée.

    Principes Fondamentaux C

    Concept Description Impact
    Pointeurs Variables stockant l'adresse mémoire d'une autre variable. Opérateurs & (adresse) et * (valeur pointée). Gestion fine de la mémoire, passage par référence.
    Gestion Mémoire Manuelle. Le programmeur alloue (malloc) et libère (free). Pas de Garbage Collector. Performance maximale, mais risques de fuites (leaks) et de segfaults.
    Compilation Code source (.c) → Compilation (gcc) → Exécutable binaire. Rapide à l'exécution, dépendant de l'architecture.
    Concept/Fonction Détail Exemple
    printf() Affichage formaté à l'écran. Utilise des spécificateurs : %d (int), %f (float), %s (string), %c (char) printf("Age: %d", age);
    scanf() Lecture formatée depuis l'entrée standard. Nécessite l'opérateur & (adresse) pour les variables non-pointeurs scanf("%d", &age);
    main() Point d'entrée obligatoire du programme. Retourne int (0 = succès, autre = erreur) int main() { return 0; }
    malloc() Allocation dynamique de mémoire (non initialisée). Retourne un pointeur void* à caster int* ptr = (int*)malloc(5 * sizeof(int));
    calloc() Allocation dynamique de mémoire initialisée à zéro. Prend 2 paramètres : nombre d'éléments et taille int* ptr = (int*)calloc(5, sizeof(int));
    realloc() Redimensionne un bloc mémoire déjà alloué tout en conservant les données existantes ptr = (int*)realloc(ptr, 10 * sizeof(int));
    free() Libère la mémoire allouée dynamiquement. Évite les fuites mémoire (memory leaks) free(ptr); ptr = NULL;
    strlen() Retourne la longueur d'une chaîne (sans le caractère nul \0) int len = strlen("Hello"); // 5
    strcpy() Copie une chaîne source vers une destination (attention aux débordements !) strcpy(dest, src);
    strcat() Concatène (ajoute) une chaîne à la fin d'une autre strcat(dest, " World");
    strcmp() Compare deux chaînes. Retourne 0 si égales, négatif si str1 < str2, positif si str1 > str2 if(strcmp(s1, s2) == 0)
    fopen() Ouvre un fichier. Modes : "r" (lecture), "w" (écriture), "a" (ajout) FILE* f = fopen("data.txt", "r");
    fclose() Ferme un fichier ouvert. Libère les ressources associées fclose(f);
    fprintf() / fscanf() Écriture/Lecture formatée dans un fichier (comme printf/scanf mais vers fichier) fprintf(f, "%d", value);
    atoi() Convertit une chaîne en entier (int). Retourne 0 en cas d'erreur (peu fiable) int n = atoi("123"); // 123
    atof() Convertit une chaîne en nombre à virgule flottante (double) double d = atof("3.14");
    rand() / srand() Génère des nombres pseudo-aléatoires. srand() initialise le générateur (avec time(NULL)) srand(time(NULL)); int r = rand() % 100;
    Exemple complet - Pointeurs et Allocation :
    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        // 1. Allocation dynamique d'un tableau de 3 entiers
        int *nombres = (int*)malloc(3 * sizeof(int));
        
        if (nombres == NULL) {
            printf("Erreur d'allocation\n");
            return 1;
        }
    
        // 2. Remplissage via pointeur
        nombres[0] = 10;
        nombres[1] = 20;
        nombres[2] = 30;
    
        // 3. Affichage avec arithmétique de pointeurs
        for (int i = 0; i < 3; i++) {
            printf("Valeur %d: %d\n", i, *(nombres + i));
        }
    
        // 4. Libération de la mémoire OBLIGATOIRE
        free(nombres);
        
        return 0;
    }
    
    // Résultat :
    // Valeur 0: 10
    // Valeur 1: 20
    // Valeur 2: 30
    Exemple - Chaînes de caractères :
    #include <stdio.h>
    #include <string.h>
    
    int main() {
        char source[] = "Hello";
        char destination[20]; // Attention à la taille !
        
        // Copie
        strcpy(destination, source);
        printf("Copie: %s\n", destination);
        
        // Concaténation
        strcat(destination, " World");
        printf("Concat: %s\n", destination);
        
        // Longueur
        printf("Longueur: %lu\n", strlen(destination));
        
        // Comparaison
        if (strcmp(destination, "Hello World") == 0) {
            printf("Les chaînes sont identiques\n");
        }
        
        return 0;
    }
    
    // Résultat :
    // Copie: Hello
    // Concat: Hello World
    // Longueur: 11
    // Les chaînes sont identiques

    Java - Points Clés

    Java : Langage orienté objet, compilé en bytecode et exécuté sur la JVM (Java Virtual Machine). Portable ("Write Once, Run Anywhere").

    Principes Fondamentaux Java

    Concept Description Exemple
    POO Tout est objet (sauf types primitifs). Encapsulation, Héritage, Polymorphisme. class Voiture extends Vehicule {}
    JVM Java Virtual Machine. Exécute le bytecode compilé (.class). Permet la portabilité. javac Main.javajava Main
    Garbage Collector Gestion automatique de la mémoire. Libère les objets non référencés. Pas besoin de free() comme en C.
    == vs equals() :
    == : compare les références (adresses mémoire) - utilise pour les types primitifs
    equals() : compare le contenu des objets - obligatoire pour String et objets
    Pour String : toujours utiliser equals() pour comparer le texte
    Exemple - Piège des Strings :
    String s1 = new String("hello");
    String s2 = new String("hello");
    String s3 = "hello";
    String s4 = "hello"; // Pool de strings
    
    // Comparaison de références (adresses)
    System.out.println(s1 == s2);       // false (2 objets différents)
    System.out.println(s3 == s4);       // true (même objet dans le pool)
    
    // Comparaison de contenu
    System.out.println(s1.equals(s2));  // true (même contenu)
    System.out.println(s1.equals(s3));  // true
    Méthode/Concept Action Exemple
    System.out.println() Affiche une ligne à la console avec retour à la ligne System.out.println("Hello");
    Scanner Classe pour lire l'entrée utilisateur. Méthodes : nextInt(), nextLine(), nextDouble() Scanner sc = new Scanner(System.in);
    int x = sc.nextInt();
    String.length() Retourne le nombre de caractères de la chaîne int len = "Hello".length(); // 5
    String.substring(0, 5) Extrait une sous-chaîne (index début inclus, index fin exclus) "Hello".substring(0, 2); // "He"
    String.equals() Compare le contenu de deux chaînes (retourne boolean) if(s1.equals(s2))
    String.charAt(i) Retourne le caractère à l'index spécifié char c = "Hello".charAt(0); // 'H'
    String.indexOf() Retourne l'index de la première occurrence d'une sous-chaîne (-1 si non trouvé) "Hello".indexOf("l"); // 2
    String.toUpperCase() / toLowerCase() Convertit la chaîne en majuscules / minuscules "hello".toUpperCase(); // "HELLO"
    String.trim() Supprime les espaces au début et à la fin de la chaîne " hello ".trim(); // "hello"
    String.split() Divise la chaîne en tableau selon un délimiteur (regex) "a,b,c".split(","); // ["a", "b", "c"]
    String.replace() Remplace toutes les occurrences d'un caractère/sous-chaîne "Hello".replace('l', 'x'); // "Hexxo"
    ArrayList<T> Tableau dynamique redimensionnable. Import : java.util.ArrayList ArrayList<String> list = new ArrayList<>();
    ArrayList.add() Ajoute un élément à la fin de la liste list.add("Element");
    ArrayList.add(index, element) Insère un élément à l'index spécifié (décale les autres) list.add(0, "Premier");
    ArrayList.get(index) Récupère l'élément à l'index spécifié String s = list.get(0);
    ArrayList.set(index, element) Remplace l'élément à l'index spécifié list.set(0, "Nouveau");
    ArrayList.remove(index) / remove(Object) Supprime l'élément à l'index ou l'objet spécifié list.remove(0); list.remove("A");
    ArrayList.size() Retourne le nombre d'éléments dans la liste int n = list.size();
    ArrayList.clear() Vide complètement la liste (supprime tous les éléments) list.clear();
    ArrayList.contains() Vérifie si la liste contient l'élément spécifié (retourne boolean) if(list.contains("A"))
    ArrayList.indexOf() Retourne l'index de la première occurrence (-1 si non trouvé) int idx = list.indexOf("A");
    Collections.sort() Trie une liste (ordre naturel). Import : java.util.Collections Collections.sort(list);
    Integer.parseInt() Convertit une String en int (lance NumberFormatException si invalide) int n = Integer.parseInt("123");
    Double.parseDouble() Convertit une String en double double d = Double.parseDouble("3.14");
    Math.random() Génère un nombre aléatoire entre 0.0 (inclus) et 1.0 (exclus) int r = (int)(Math.random() * 100); // 0-99
    Math.max() / Math.min() Retourne le maximum/minimum de deux valeurs int max = Math.max(5, 10); // 10
    Exemple complet - Classes et ArrayList :
    import java.util.ArrayList;
    import java.util.Collections;
    
    public class Main {
        public static void main(String[] args) {
            // Création d'une liste
            ArrayList<String> langages = new ArrayList<>();
            
            // Ajout
            langages.add("Java");
            langages.add("Python");
            langages.add("C++");
            
            // Affichage
            System.out.println("Liste: " + langages);
            
            // Accès et modification
            langages.set(1, "JavaScript");
            System.out.println("Modifié: " + langages.get(1));
            
            // Tri
            Collections.sort(langages);
            System.out.println("Trié: " + langages);
            
            // Boucle
            for (String lang : langages) {
                System.out.println("- " + lang);
            }
            
            // Taille et suppression
            System.out.println("Taille: " + langages.size());
            langages.remove("C++");
            System.out.println("Après suppression: " + langages);
        }
    }
    
    // Résultat :
    // Liste: [Java, Python, C++]
    // Modifié: JavaScript
    // Trié: [C++, Java, JavaScript]
    // - C++
    // - Java
    // - JavaScript
    // Taille: 3
    // Après suppression: [Java, JavaScript]

    Python - Points Clés

    Python : Langage de programmation interprété, orienté objet, avec une syntaxe claire et concise. Typage dynamique.

    Principes Fondamentaux Python

    Concept Description Exemple
    Indentation Définit les blocs de code (pas d'accolades {}). Obligatoire et stricte. if True:
    print("OK") # 4 espaces
    Typage Dynamique Le type est déterminé à l'exécution. Une variable peut changer de type. x = 5 # int
    x = "hello" # str
    Everything is Object Tout (nombres, fonctions, classes) est un objet avec des méthodes. print(type(5)) # <class 'int'>
    list vs tuple :
    list [1, 2, 3] : Mutable (modifiable) - peut changer après création
    tuple (1, 2, 3) : Immutable (non modifiable) - constante après création
    Exemple - Mutable vs Immutable :
    # LIST (Mutable)
    liste = [1, 2, 3]
    liste[0] = 99    # OK
    liste.append(4)  # OK
    print(liste)     # [99, 2, 3, 4]
    
    # TUPLE (Immutable)
    tup = (1, 2, 3)
    # tup[0] = 99    # Erreur ! TypeError
    # tup.append(4)  # Erreur ! AttributeError
    
    # Intérêt du tuple : clé de dictionnaire, retour multiple sécurisé
    coordonnees = (10.5, 20.3)
    x, y = coordonnees  # Unpacking
    print(f"x={x}, y={y}") # x=10.5, y=20.3
    Fonction/Méthode Action Exemple
    print() Affiche des valeurs à l'écran. Accepte plusieurs arguments séparés par virgule print("Hello", name)
    input() Lit une ligne depuis l'entrée standard (retourne toujours une String) name = input("Nom: ")
    len() Retourne la longueur (nombre d'éléments) d'une liste, chaîne, tuple, dict, etc. len([1, 2, 3]) # 3
    type() Retourne le type de l'objet (utile pour le débogage) type(42) # <class 'int'>
    int() / float() / str() Conversion de types (casting). int() tronque la partie décimale int("42") # 42
    float("3.14") # 3.14
    range(start, stop, step) Génère une séquence d'entiers (stop exclus). Par défaut start=0, step=1 range(5) # 0,1,2,3,4
    range(1, 10, 2) # 1,3,5,7,9
    enumerate() Retourne un itérable avec index et valeur (utile dans les boucles for) for i, val in enumerate(['a','b']):
    print(i, val)
    zip() Combine plusieurs listes en paires (s'arrête à la plus courte) list(zip([1,2], ['a','b'])) # [(1,'a'), (2,'b')]
    map() Applique une fonction à chaque élément d'un itérable list(map(str, [1, 2])) # ['1', '2']
    filter() Filtre les éléments selon une condition (fonction retournant bool) list(filter(lambda x: x>0, [-1, 1, 2])) # [1, 2]
    sum() / max() / min() Opérations d'agrégation sur les itérables sum([1, 2, 3]) # 6
    max([1, 5, 2]) # 5
    sorted() Retourne une nouvelle liste triée (sans modifier l'original) sorted([3, 1, 2]) # [1, 2, 3]
    list.append(x) Ajoute x à la FIN de la liste (modifie la liste en place) lst = [1, 2]
    lst.append(3) # [1, 2, 3]
    list.insert(i, x) Insère x à l'INDEX i (décale les éléments suivants) lst.insert(0, 'a') # ['a', 1, 2, 3]
    list.remove(x) Supprime la première occurrence de x (erreur si x non trouvé) lst.remove('a')
    list.pop(i) Supprime et retourne l'élément à l'index i (dernier par défaut) x = lst.pop() # supprime dernier
    list.sort() Trie la liste en place (modifie l'original, retourne None) lst.sort() # lst modifiée
    list.reverse() Inverse l'ordre des éléments en place lst.reverse()
    list.index(x) Retourne l'index de la première occurrence de x lst.index(2) # retourne position de 2
    list.count(x) Compte le nombre d'occurrences de x dans la liste lst.count(1) # nombre de 1 dans lst
    list.extend(iterable) Ajoute tous les éléments d'un itérable à la fin de la liste lst.extend([4, 5])
    list.copy() Crée une copie superficielle (shallow copy) de la liste new_lst = lst.copy()
    list.clear() Vide complètement la liste (supprime tous les éléments) lst.clear() # lst devient []
    str.strip() Enlève les espaces (et caractères spécifiés) du début et de la fin " hello ".strip() # "hello"
    str.split(sep) Divise la chaîne en liste selon le séparateur (espace par défaut) "a,b,c".split(",") # ['a', 'b', 'c']
    str.join(iterable) Joint les éléments d'un itérable avec la chaîne comme séparateur ",".join(['a', 'b']) # "a,b"
    str.replace(old, new) Remplace toutes les occurrences de old par new "Hello".replace('l', 'x') # "Hexxo"
    str.upper() / str.lower() Convertit en majuscules / minuscules "hello".upper() # "HELLO"
    str.startswith() / str.endswith() Vérifie si la chaîne commence/finit par une sous-chaîne "hello".startswith("he") # True
    dict.get(key, default) Récupère la valeur d'une clé (retourne default si clé absente) d.get('x', 0) # 0 si 'x' absent
    dict.keys() / dict.values() Retourne les clés / valeurs du dictionnaire list(d.keys()) # liste des clés
    dict.items() Retourne les paires (clé, valeur) pour itération for k, v in d.items():
    open(file, mode) Ouvre un fichier. Modes : 'r' (lecture), 'w' (écriture), 'a' (ajout) f = open('data.txt', 'r')
    file.read() / file.write() Lit tout le contenu / Écrit une chaîne dans le fichier content = f.read()
    f.write("text")
    file.readline() Lit une ligne du fichier (avec le \n à la fin) line = f.readline()
    file.close() Ferme le fichier (libère les ressources) f.close()
    with open(...) as f: Gestionnaire de contexte - ferme automatiquement le fichier with open('f.txt') as f:
    content = f.read()
    Exemple complet - Listes, Dicts et Slicing :
    # SLICING (Découpage)
    # syntaxe: liste[start:stop:step]
    nombres = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    print(nombres[2:5])   # [2, 3, 4] (stop exclus)
    print(nombres[:3])    # [0, 1, 2] (début jusqu'à 3)
    print(nombres[7:])    # [7, 8, 9] (de 7 à la fin)
    print(nombres[::2])   # [0, 2, 4, 6, 8] (un sur deux)
    print(nombres[::-1])  # [9, 8, 7... 0] (inversé)
    
    # LIST COMPREHENSION (Création concise)
    carres = [x**2 for x in range(5)]
    print(carres)  # [0, 1, 4, 9, 16]
    
    pairs = [x for x in range(10) if x % 2 == 0]
    print(pairs)  # [0, 2, 4, 6, 8]
    
    # DICTIONNAIRE
    etudiant = {"nom": "Ali", "age": 22, "cours": ["Math", "Info"]}
    print(etudiant["nom"])             # "Ali"
    print(etudiant.get("moyenne", 0))  # 0 (clé absente, défaut retourné)
    
    # Itération
    for cle, val in etudiant.items():
        print(f"{cle}: {val}")
    
    # Résultat :
    # nom: Ali
    # age: 22
    # cours: ['Math', 'Info']

    PHP - Points Clés

    PHP : Langage de script côté serveur, spécialisé pour le développement web. S'exécute sur le serveur avant d'envoyer le HTML au client.

    Principes Fondamentaux PHP

    Concept Description Exemple
    Syntaxe $ Toutes les variables commencent par le symbole dollar. Sensible à la casse. $nom = "Ali";
    Serveur → Client PHP génère du HTML. Le client ne voit jamais le code PHP, seulement le résultat. <?php echo "<b>Texte</b>"; ?>
    Tableaux Associatifs Clés personnalisées (string). Très utilisés (BDD, JSON, Config). $user = ["nom" => "Ali", "age" => 25];
    Concept/Fonction Détail Exemple
    echo Affiche une ou plusieurs chaînes (rapide, pas de retour de valeur). Accepte plusieurs paramètres séparés par virgule echo "Hello", " ", "World";
    print Affiche une chaîne, retourne toujours 1 (plus lent qu'echo). Accepte un seul argument print "Hello World";
    var_dump() Affiche les informations détaillées d'une variable (type et valeur). Utile pour le débogage var_dump($array); // affiche type et contenu
    print_r() Affiche une représentation lisible d'une variable (pratique pour les tableaux) print_r($array);
    Variables Toutes les variables commencent par le symbole $. Typage faible (type déterminé automatiquement) $nom = "John";
    $age = 25;
    $_GET / $_POST Tableaux superglobaux contenant les données envoyées par GET (URL) ou POST (formulaire) $nom = $_POST['nom'];
    $_SESSION Tableau superglobal pour stocker des données de session (persistantes entre pages). Nécessite session_start() session_start();
    $_SESSION['user'] = 'John';
    array_push() Ajoute un ou plusieurs éléments à la FIN d'un tableau array_push($arr, "a", "b");
    array_pop() Supprime et retourne le DERNIER élément du tableau $last = array_pop($arr);
    array_unshift() Ajoute un ou plusieurs éléments au DÉBUT du tableau array_unshift($arr, "premier");
    array_shift() Supprime et retourne le PREMIER élément du tableau $first = array_shift($arr);
    count() / sizeof() Retourne le nombre d'éléments dans un tableau (count() est préféré) $n = count($arr);
    in_array() Vérifie si une valeur existe dans le tableau (retourne boolean) if(in_array("a", $arr))
    array_search() Cherche une valeur et retourne sa clé (ou false si non trouvé) $key = array_search("a", $arr);
    array_slice() Extrait une portion de tableau (comme substring pour les tableaux) $sub = array_slice($arr, 0, 3);
    array_splice() Supprime/remplace des éléments à une position arbitraire array_splice($arr, 1, 2, ["x"]);
    array_merge() Fusionne un ou plusieurs tableaux en un seul $combined = array_merge($a, $b);
    array_map() Applique une fonction de rappel à chaque élément du tableau $squares = array_map(fn($x) => $x*$x, $nums);
    array_filter() Filtre les éléments selon une fonction de rappel $even = array_filter($nums, fn($x) => $x % 2 == 0);
    array_reduce() Réduit un tableau à une valeur unique via une fonction cumulatrice $sum = array_reduce($nums, fn($c, $i) => $c + $i, 0);
    sort() / rsort() Trie un tableau par valeurs en ordre croissant / décroissant (réindexe les clés) sort($arr); // tri croissant
    asort() / arsort() Trie par valeurs en conservant les associations clé-valeur asort($assoc);
    ksort() / krsort() Trie un tableau par clés (croissant / décroissant) ksort($assoc);
    explode() Divise une chaîne en tableau selon un délimiteur $arr = explode(",", "a,b,c"); // ["a", "b", "c"]
    implode() / join() Joint les éléments d'un tableau en une chaîne avec un séparateur $str = implode(", ", $arr);
    strlen() Retourne la longueur d'une chaîne $len = strlen("Hello"); // 5
    strpos() Retourne la position de la première occurrence d'une sous-chaîne (ou false) $pos = strpos("Hello", "l"); // 2
    stripos() Comme strpos() mais insensible à la casse $pos = stripos("Hello", "L"); // 2
    substr() Extrait une partie d'une chaîne (start, length) $sub = substr("Hello", 0, 2); // "He"
    str_replace() Remplace toutes les occurrences d'une sous-chaîne par une autre $s = str_replace("l", "x", "Hello"); // "Hexxo"
    strtolower() / strtoupper() Convertit une chaîne en minuscules / majuscules $s = strtoupper("hello"); // "HELLO"
    trim() Supprime les espaces (et caractères spécifiés) du début et de la fin $s = trim(" hello "); // "hello"
    ltrim() / rtrim() Supprime les espaces à gauche (début) / à droite (fin) uniquement $s = ltrim(" hello");
    PDO Extension pour l'accès aux bases de données (MySQL, PostgreSQL, etc.). Supporte les requêtes préparées (sécurité contre SQL injection) $pdo = new PDO("mysql:host=localhost;dbname=test", $user, $pass);
    $pdo->prepare() Prépare une requête SQL avec des paramètres nommés (?) ou positionnels (:nom) $stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
    $stmt->bindParam() Lie une variable à un paramètre de la requête préparée $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->execute() Exécute la requête préparée $stmt->execute();
    $stmt->fetch() Récupère la ligne suivante du résultat (PDO::FETCH_ASSOC pour tableau associatif) $row = $stmt->fetch(PDO::FETCH_ASSOC);
    $stmt->fetchAll() Récupère toutes les lignes du résultat dans un tableau $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    $pdo->query() Exécute une requête SQL et retourne un objet PDOStatement (pour SELECT simple) $stmt = $pdo->query("SELECT * FROM users");
    $pdo->exec() Exécute une requête SQL et retourne le nombre de lignes affectées (pour INSERT/UPDATE/DELETE) $count = $pdo->exec("DELETE FROM users WHERE id=1");
    $pdo->lastInsertId() Retourne l'ID auto-incrémenté généré par la dernière requête INSERT $id = $pdo->lastInsertId();
    json_encode() Convertit une valeur PHP en chaîne JSON $json = json_encode(['name' => 'John']);
    json_decode() Décode une chaîne JSON en valeur PHP (true pour tableau associatif) $arr = json_decode($json, true);
    file_get_contents() Lit tout le contenu d'un fichier dans une chaîne $content = file_get_contents('file.txt');
    file_put_contents() Écrit une chaîne dans un fichier (crée le fichier si inexistant) file_put_contents('file.txt', 'Hello');
    Exemple complet - Tableaux Associatifs et Boucles :
    <?php
    // Tableau associatif (clés string)
     $produits = [
        ["nom" => "Laptop", "prix" => 999.99, "stock" => 5],
        ["nom" => "Souris", "prix" => 29.99, "stock" => 50],
        ["nom" => "Clavier", "prix" => 79.99, "stock" => 20]
    ];
    
    // Parcourir avec foreach
    foreach ($produits as $p) {
        echo $p["nom"] . " - " . $p["prix"] . "€\n";
    }
    
    // Filtrer les produits chers
     $chers = array_filter($produits, fn($p) => $p["prix"] > 50);
    print_r(array_column($chers, "nom"));
    
    // Calculer le stock total
     $totalStock = array_sum(array_column($produits, "stock"));
    echo "Stock total: $totalStock\n";
    
    // JSON
     $json = json_encode($produits);
    echo $json; // [{"nom":"Laptop",...}]
    ?>
    
    // Résultat :
    // Laptop - 999.99€
    // Souris - 29.99€
    // Clavier - 79.99€
    // Array ( [0] => Laptop [1] => Clavier )
    // Stock total: 75
    // [{"nom":"Laptop","prix":999.99,"stock":5},...]
    Exemple - Connexion PDO et Requêtes Préparées :
    <?php
    // Configuration (normalement dans un fichier séparé)
     $host = 'localhost';
     $db   = 'ma_base';
     $user = 'root';
     $pass = '';
    
    try {
        // Connexion
        $pdo = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $pass);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
        // Requête préparée (SÉCURISÉ contre injections SQL)
        $stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
        $stmt->execute(['email' => 'ali@test.com']);
        
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($user) {
            echo "Bienvenue " . htmlspecialchars($user['nom']);
        } else {
            echo "Utilisateur introuvable";
        }
    
        // Insertion
        $sql = "INSERT INTO users (nom, email) VALUES (:nom, :email)";
        $stmt = $pdo->prepare($sql);
        $stmt->execute([
            'nom' => 'Sara',
            'email' => 'sara@test.com'
        ]);
        
        echo "Nouvel ID: " . $pdo->lastInsertId();
    
    } catch (PDOException $e) {
        echo "Erreur: " . $e->getMessage();
    }
    ?>

    ⚛️ React.js

    Définition : Bibliothèque JavaScript pour construire des interfaces utilisateur interactives (SPA) - Développée par Facebook (Meta). Utilise le Virtual DOM pour des mises à jour efficaces.

    JSX Règles

    Règle Explication Exemple Code
    Parent unique Un seul élément racine obligatoire return (<div><h1>Titre</h1><p>Texte</p></div>);
    Fragment Utiliser <></> pour wrapper sans div return (<><h1>A</h1><h1>B</h1></>);
    className Pas de class (mot réservé JS) <div className="container">
    Expressions JS Variables et calculs entre {} <p>{nom} a {age} ans</p>
    CamelCase Attributs en camelCase <label htmlFor="input"> ou <button onClick={click}>

    Composants

    Type Code Exemple
    Fonctionnel (recommandé)
    function Welcome({ name }) {
      return <h1>Bonjour {name}</h1>;
    }
    
    // Utilisation : <Welcome name="John" />
    Flèche (Arrow)
    const Welcome = ({ name }) => {
      return <h1>Bonjour {name}</h1>;
    };
    Classe (ancien)
    class Welcome extends React.Component {
      render() {
        return <h1>Bonjour {this.props.name}</h1>;
      }
    }

    Props vs State

    Aspect Props State
    Définition Données passées de parent → enfant Données internes au composant
    Mutabilité Immutables (lecture seule) Mutable via setter
    Code Exemple
    // Parent
    <User name="John" age={25} />
    
    // Enfant
    function User(props) {
      return <p>{props.name}</p>;
    }
    const [count, setCount] = useState(0);
    
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>

    Hook useState - Exemples

    Cas Code Piège / Note
    Compteur simple
    const [count, setCount] = useState(0);
    
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>
    setState est asynchrone !
    Forme fonctionnelle (correct)
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
    Quand la nouvelle valeur dépend de l'ancienne, toujours utiliser la forme fonctionnelle pour éviter les bugs
    Objet state
    const [user, setUser] = useState({
      name: 'John',
      age: 25
    });
    
    // Mise à jour
    setUser({ ...user, age: 26 });
    Spread operator (...) pour garder les anciennes valeurs !
    Tableau state
    const [items, setItems] = useState(['a', 'b']);
    
    // Ajouter
    setItems([...items, 'c']);
    
    // Supprimer
    setItems(items.filter(i => i !== 'b'));
    Ne jamais modifier directement : items.push() ❌

    Hook useEffect - Exemples

    Cas d'usage Code Quand exécuté ?
    Au montage seulement
    useEffect(() => {
      console.log('Composant monté');
    }, []);
    Une fois après le premier render (componentDidMount)
    Surveillance variable
    useEffect(() => {
      console.log('count a changé:', count);
    }, [count]);
    À chaque changement de count
    API / Fetch données
    useEffect(() => {
      fetch('https://api.com/users')
        .then(r => r.json())
        .then(data => setUsers(data));
    }, []);
    Une fois au montage (tableau vide)
    Cleanup / Nettoyage
    useEffect(() => {
      const timer = setInterval(() => {...}, 1000);
    
      return () => {
        clearInterval(timer); // Cleanup
      };
    }, []);
    Return = cleanup exécuté avant prochain effet ou démontage
    Subscription
    useEffect(() => {
      const sub = websocket.subscribe(msg => {...});
    
      return () => sub.unsubscribe();
    }, []);
    Essentiel pour éviter les fuites mémoire !

    Hook useRef - Exemples

    Usage Code Note
    Référence DOM (focus)
    const inputRef = useRef(null);
    
    useEffect(() => {
      inputRef.current.focus();
    }, []);
    
    return <input ref={inputRef} />;
    Accès direct au nœud DOM
    Valeur persistante
    const renderCount = useRef(0);
    renderCount.current++;
    
    console.log('Renders:', renderCount.current);
    Ne cause pas de re-render quand modifié
    Stocker valeur précédente
    const prevCount = useRef();
    useEffect(() => {
      prevCount.current = count;
    }, [count]);
    
    // prevCount.current = ancienne valeur
    Comparaison ancienne/nouvelle valeur

    Autres Hooks - Exemples

    Hook Code Exemple Usage
    useContext
    const ThemeContext = createContext('light');
    
    // Dans composant enfant :
    const theme = useContext(ThemeContext);
    // Retourne 'light' ou valeur du Provider
    Partager données sans props drilling
    useReducer
    const [state, dispatch] = useReducer(
      (state, action) => {
        switch(action.type) {
          case 'inc': return { count: state.count + 1 };
          default: return state;
        }
      },
      { count: 0 }
    );
    
    // dispatch({ type: 'inc' })
    Logique d'état complexe (Redux léger)
    useMemo
    const expensive = useMemo(() => {
      return data.filter(x => x > 0).map(x => x * 2);
    }, [data]);
    Cache résultat calcul coûteux
    useCallback
    const handleClick = useCallback(() => {
      console.log('clicked');
    }, []); // Même référence entre renders
    Cache fonction pour enfants optimisés

    Rendu de Listes

    Règle Code Exemple ⚠️ Erreur à éviter
    Key unique obligatoire
    {users.map(user => (
      <li key={user.id}>{user.name}</li>
    ))}
    key={index} - cause bugs si ordre change
    Key stable
    // ✅ ID de la base de données
    key={item.id}
    
    // ✅ Générer UUID si pas d'ID
    key={crypto.randomUUID()}
    La key doit être stable entre les renders

    Gestion des Événements

    Événement Code Exemple Note
    onClick
    <button onClick={() => alert('Cliqué!')}>
      Cliquez
    </button>
    
    // OU avec fonction :
    <button onClick={handleClick}>...
    Pas de () dans onClick={} sauf si arrow function
    onChange (input)
    const [text, setText] = useState('');
    
    <input 
      value={text}
      onChange={(e) => setText(e.target.value)}
    />
    e.target.value = valeur saisie
    onSubmit (formulaire)
    <form onSubmit={(e) => {
      e.preventDefault(); // Empêche reload
      console.log('Envoyé:', text);
    }}>
      <input ... />
      <button type="submit">Envoyer</button>
    </form>
    e.preventDefault() obligatoire !
    Event object
    (e) => {
      e.preventDefault(); // Annule action par défaut
      e.stopPropagation(); // Stop bubbling
      e.target.value; // Valeur élément
    }
    e = événement synthétique React

    Formulaires Contrôlés vs Non-contrôlés

    Type Code Avantage
    Contrôlé (recommandé)
    const [value, setValue] = useState('');
    
    <input 
      value={value}              // Contrôlé par React
      onChange={(e) => setValue(e.target.value)}
    />
    Validation temps réel, contrôle total, valeur garantie
    Non-contrôlé
    const inputRef = useRef(null);
    
    <input ref={inputRef} />
    
    // Lire valeur :
    const value = inputRef.current.value;
    Plus simple pour cas basiques, moins de code

    Conditional Rendering

    Méthode Code Usage
    If simple
    {isLoggedIn && <LogoutButton />}
    Affiche si condition vraie
    If-Else
    {isLoggedIn 
      ? <LogoutButton /> 
      : <LoginButton />}
    Choix entre deux éléments
    Variable
    let button;
    if (isLoggedIn) {
      button = <LogoutButton />;
    } else {
      button = <LoginButton />;
    }
    
    return <div>{button}</div>;
    Logique complexe avant return
    Règles des Hooks :
    1. Appeler les hooks uniquement au niveau racine (pas dans boucles, conditions, ou fonctions imbriquées)
    2. Appeler les hooks uniquement dans des composants React ou hooks personnalisés
    3. L'ordre des hooks doit être identique à chaque render

    🅰️ Angular

    Définition : Framework TypeScript complet développé par Google pour applications d'entreprise (SPA). Architecture basée sur des composants réutilisables avec injection de dépendances native.

    Commandes CLI Essentielles

    Commande Action Exemple / Options
    ng new projet Créer nouvelle application ng new mon-app --routing --style=scss
    ng serve Lancer serveur dev (hot reload) ng serve --open --port 4200 (ou -o)
    ng generate component nom Générer composant (+ .html, .css, .spec.ts) ng g c user-profile (g = generate)
    ng generate service nom Générer service injectable ng g s services/auth
    ng generate module nom Générer module feature ng g m admin --routing
    ng build Compiler pour production ng build --configuration production (output dans /dist)
    ng test Exécuter tests unitaires (Vitest/Jasmine) ng test --watch=false (une seule exécution)
    ng add package Installer et configurer librairie ng add @angular/material
    ng update Mettre à jour Angular et dépendances ng update @angular/core @angular/cli

    Structure d'un Composant

    Fichier Rôle Code Exemple
    .ts Logique TypeScript
    @Component({
      selector: 'app-hello',
      templateUrl: './hello.component.html',
      styleUrls: ['./hello.component.css']
    })
    export class HelloComponent {
      title = 'Bonjour Angular!';
    }
    .html Template (vue)
    <div class="hello">
      <h1>{{ title }}</h1>
    </div>
    .css/.scss Styles scoped (uniquement ce composant)
    .hello { color: blue; }
    /* Scoped uniquement à ce composant */
    Exemple complet - Composant simple :
    // ===== user-card.component.ts =====
    import { Component, Input } from '@angular/core';
    
    @Component({
      selector: 'app-user-card',
      template: `
        <div class="card">
          <h2>{{ user.name }}</h2>
          <p>Email: {{ user.email }}</p>
        </div>
      `,
      styles: ['.card { border: 1px solid #ccc; padding: 10px; }']
    })
    export class UserCardComponent {
      @Input() user: { name: string; email: string } = { name: '', email: '' };
    }
    
    // ===== app.component.ts (Utilisation) =====
    <app-user-card [user]="currentUser"></app-user-card>
    
    // Résultat affiché :
    // +----------------------+
    // | Alice                |
    // | Email: alice@test.com|
    // +----------------------+

    Data Binding - 4 Types

    Type Syntaxe Direction Code Exemple
    Interpolation {{ expression }} C → V <h1>{{ title }}</h1> ou {{ 2 + 2 }}
    Property [property]="value" C → V <img [src]="imageUrl"> ou <button [disabled]="isDisabled">
    Event (event)="handler()" V → C <button (click)="onSave()"> ou <input (input)="onInput($event)">
    Two-way [(ngModel)]="property" C ↔ V <input [(ngModel)]="userName"> (nécessite FormsModule)
    Mnémotechnique : "Banana in a box" [()] = Property [] + Event ()
    Exemple pratique - Les 4 types de binding :
    // ===== counter.component.ts =====
    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-counter',
      template: `
        <!-- 1. Interpolation (Afficher) -->
        <p>Compteur: {{ count }}</p>
        
        <!-- 2. Property Binding (Définir propriété) -->
        <button [disabled]="count === 0" (click)="decrement()">-</button>
        <button [disabled]="count >= 10" (click)="increment()">+</button>
        
        <!-- 3. Event Binding (Écouter) -->
        <button (click)="reset()">Reset</button>
        
        <!-- 4. Two-way Binding (Input utilisateur) -->
        <label>Pas: <input type="number" [(ngModel)]="step"></label>
      `
    })
    export class CounterComponent {
      count = 0;
      step = 1;
    
      increment() { this.count += this.step; }
      decrement() { this.count -= this.step; }
      reset() { this.count = 0; }
    }
    
    // Test utilisateur :
    // 1. Clic '+' -> Affiche "Compteur: 1"
    // 2. Change "Pas" à 5 -> Clic '+' -> Affiche "Compteur: 6"
    // 3. Clic '-' -> Affiche "Compteur: 1"

    Directives Structurelles (*)

    Directive Usage Code Exemple
    *ngIf Affichage conditionnel (retire du DOM si faux)
    <div *ngIf="isLogged">Bienvenue</div>
    
    <!-- Avec else -->
    <div *ngIf="isLogged; else loginTpl">Bienvenue</div>
    <ng-template #loginTpl>Connectez-vous</ng-template>
    *ngFor Boucle sur collection
    <ul>
      <li *ngFor="let user of users; let i = index">
        {{ i + 1 }}. {{ user.name }}
      </li>
    </ul>
    [ngClass] Classes CSS dynamiques
    <div [ngClass]="{'active': isActive, 'disabled': isDisabled}">
    <div [ngClass]="['class1', 'class2']">
    [ngStyle] Styles inline dynamiques
    <div [ngStyle]="{'color': 'red', 'font-size.px': 14}">
    Exemple complet - Liste avec *ngFor et *ngIf :
    // ===== user-list.component.ts =====
    users = [
      { id: 1, name: 'Alice', active: true },
      { id: 2, name: 'Bob', active: false },
      { id: 3, name: 'Charlie', active: true }
    ];
    
    // Template
    <ul>
      <li *ngFor="let user of users; trackBy: trackById"
          [ngClass]="{'active-user': user.active, 'inactive-user': !user.active}">
        
        {{ user.name }}
        
        <span *ngIf="user.active; else inactiveBadge">
          ✅ Actif
        </span>
        <ng-template #inactiveBadge>
          ❌ Inactif
        </ng-template>
        
      </li>
    </ul>
    
    // Logique de tracking (optimisation performance)
    trackById(index: number, user: any): number {
      return user.id;
    }
    
    // Résultat :
    // • Alice ✅ Actif      (classe .active-user)
    // • Bob ❌ Inactif      (classe .inactive-user)
    // • Charlie ✅ Actif    (classe .active-user)

    Nouvelle Syntaxe Control Flow (Angular 17+)

    Directive Ancien (*ng) Nouveau (@)
    Condition
    *ngIf
    @if (isLogged) {
      <p>Bienvenue</p>
    } @else {
      <p>Connectez-vous</p>
    }
    Boucle
    *ngFor
    @for (item of items; track item.id) {
      <li>{{ item.name }}</li>
    } @empty {
      <p>Aucun élément</p>
    }
    Switch
    [ngSwitch]
    @switch (role) {
      @case ('admin') { <p>Admin</p> }
      @case ('user') { <p>User</p> }
      @default { <p>Guest</p> }
    }
    Exemple - Syntaxe moderne Angular 17+ :
    // Component
    role = 'admin';
    items = [
      { id: 1, name: 'Laptop' },
      { id: 2, name: 'Phone' }
    ];
    
    // Template
    @switch (role) {
      @case ('admin') {
        <div class="admin-panel">Panneau Admin</div>
      }
      @case ('user') {
        <div class="user-panel">Panneau User</div>
      }
      @default {
        <p>Veuillez vous connecter</p>
      }
    }
    
    @for (item of items; track item.id) {
      <div>{{ item.name }}</div>
    } @empty {
      <p>Aucun produit disponible</p>
    }
    
    // Résultat :
    // Panneau Admin
    // Laptop
    // Phone

    Cycle de Vie - Hooks

    Hook Moment Code Exemple
    ngOnInit() Après création du composant (initialisation)
    ngOnInit() {
      // Appels API, setup initial
      this.loadData();
    }
    ngOnChanges() Quand les @Input() changent
    ngOnChanges(changes: SimpleChanges) {
      console.log(changes['userName'].currentValue);
    }
    ngOnDestroy() Avant destruction (nettoyage)
    ngOnDestroy() {
      this.subscription.unsubscribe(); // ⚠️ Important !
    }
    Exemple - Ordre d'exécution des Hooks :
    import { Component, Input, OnChanges, OnInit, OnDestroy } from '@angular/core';
    
    @Component({
      selector: 'app-lifecycle',
      template: '<p>Lifecycle Demo</p>'
    })
    export class LifecycleComponent implements OnChanges, OnInit, OnDestroy {
      @Input() data: string = '';
    
      // 1. Appelé à chaque changement de @Input
      ngOnChanges() {
        console.log('1. ngOnChanges - Input modifié');
      }
    
      // 2. Appelé une seule fois après le premier ngOnChanges
      ngOnInit() {
        console.log('2. ngOnInit - Composant initialisé');
      }
    
      // 3. Appelé juste avant la destruction du composant
      ngOnDestroy() {
        console.log('3. ngOnDestroy - Composant détruit');
      }
    }
    
    // Console lors de la création :
    // "1. ngOnChanges - Input modifié"
    // "2. ngOnInit - Composant initialisé"
    
    // Console lors de la navigation vers une autre page :
    // "3. ngOnDestroy - Composant détruit"

    Communication Parent ↔ Enfant

    Direction Décorateur Code Exemple
    Parent → Enfant @Input()
    // Enfant
    @Input() userName: string;
    
    // Parent
    <app-enfant [userName]="parentName"></app-enfant>
    Enfant → Parent @Output()
    // Enfant
    @Output() notify = new EventEmitter<string>();
    send() { this.notify.emit('message'); }
    
    // Parent
    <app-enfant (notify)="onNotify($event)"></app-enfant>
    Exemple complet - Parent/Enfant :
    // ===== child.component.ts =====
    import { Component, Input, Output, EventEmitter } from '@angular/core';
    
    @Component({
      selector: 'app-child',
      template: `
        <div>
          <h3>Enfant</h3>
          <p>Message du parent: {{ message }}</p>
          <button (click)="replyToParent()">Répondre</button>
        </div>
      `
    })
    export class ChildComponent {
      @Input() message: string = '';          // Reçoit du parent
      @Output() response = new EventEmitter<string>(); // Envoie au parent
    
      replyToParent() {
        this.response.emit('Salut Papa !');
      }
    }
    
    // ===== parent.component.ts =====
    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-parent',
      template: `
        <div>
          <h2>Parent</h2>
          <p>Réponse de l'enfant: {{ childResponse }}</p>
          
          <!-- Envoi de données [message] / Réception (response) -->
          <app-child 
            [message]="'Bonjour Fils'" 
            (response)="onResponse($event)">
          </app-child>
        </div>
      `
    })
    export class ParentComponent {
      childResponse = '';
    
      onResponse(msg: string) {
        this.childResponse = msg;
      }
    }
    
    // Résultat affiché :
    // Parent
    // Réponse de l'enfant: Salut Papa ! (après clic)
    //   Enfant
    //   Message du parent: Bonjour Fils
    //   [Répondre]

    Services et Dependency Injection

    Concept Code Exemple Note
    Création Service
    @Injectable({
      providedIn: 'root'  // Singleton global
    })
    export class UserService {
      getUsers() {
        return ['Alice', 'Bob'];
      }
    }
    providedIn: 'root' = une seule instance
    Injection (classique)
    constructor(private userService: UserService) {}
    Pattern recommandé
    Injection (moderne)
    private userService = inject(UserService);
    Angular 14+
    Exemple complet - Service et Injection :
    // ===== user.service.ts =====
    import { Injectable } from '@angular/core';
    import { Observable, of } from 'rxjs';
    
    @Injectable({
      providedIn: 'root' // Singleton disponible partout
    })
    export class UserService {
      private users = [
        { id: 1, name: 'Alice', role: 'Admin' },
        { id: 2, name: 'Bob', role: 'User' }
      ];
    
      getUsers(): Observable<any[]> {
        return of(this.users); // Simule un appel HTTP
      }
    }
    
    // ===== app.component.ts =====
    import { Component, OnInit } from '@angular/core';
    import { UserService } from './user.service';
    
    @Component({
      selector: 'app-root',
      template: `
        <ul>
          <li *ngFor="let user of users">{{ user.name }} ({{ user.role }})</li>
        </ul>
      `
    })
    export class AppComponent implements OnInit {
      users: any[] = [];
    
      // Injection du service via le constructeur
      constructor(private userService: UserService) {}
    
      ngOnInit() {
        this.userService.getUsers().subscribe(data => {
          this.users = data;
        });
      }
    }

    Routing

    Concept Code Exemple
    Configuration routes
    const routes: Routes = [
      { path: '', redirectTo: '/home', pathMatch: 'full' },
      { path: 'home', component: HomeComponent },
      { path: 'users/:id', component: UserDetailComponent },
      { path: '**', component: NotFoundComponent }  // Wildcard dernier !
    ];
    Navigation template
    <a routerLink="/home">Accueil</a>
    <a [routerLink]="['/users', userId]">Détail</a>
    <a routerLink="/home" routerLinkActive="active">Accueil</a>
    Navigation programmatique
    constructor(private router: Router) {}
    
    goToDetail(id: number) {
      this.router.navigate(['/users', id]);
    }
    Exemple - Mise en place du Router :
    // ===== app-routing.module.ts =====
    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    import { HomeComponent } from './home/home.component';
    import { UserDetailComponent } from './user-detail/user-detail.component';
    
    const routes: Routes = [
      { path: '', redirectTo: 'home', pathMatch: 'full' },
      { path: 'home', component: HomeComponent },
      { path: 'user/:id', component: UserDetailComponent }, // Route paramétrée
      { path: '**', redirectTo: 'home' } // Fallback
    ];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }
    
    // ===== app.component.html =====
    <nav>
      <a routerLink="/home" routerLinkActive="active">Home</a>
      <a routerLink="/user/1">User 1</a>
    </nav>
    
    <router-outlet></router-outlet> 
    <!-- Les composants s'affichent ici -->
    
    // ===== user-detail.component.ts (Lecture paramètre) =====
    import { ActivatedRoute } from '@angular/router';
    
    constructor(private route: ActivatedRoute) {}
    
    ngOnInit() {
      const id = this.route.snapshot.paramMap.get('id');
      console.log('ID utilisateur:', id); // "1"
    }

    HTTP Client - Requêtes

    Méthode Code Exemple
    GET
    getUsers(): Observable<User[]> {
      return this.http.get<User[]>('https://api.example.com/users');
    }
    POST
    createUser(user: User): Observable<User> {
      return this.http.post<User>('/api/users', user);
    }
    PUT
    updateUser(id: number, user: User): Observable<User> {
      return this.http.put<User>(`/api/users/${id}`, user);
    }
    DELETE
    deleteUser(id: number): Observable<void> {
      return this.http.delete<void>(`/api/users/${id}`);
    }
    Subscribe
    this.api.getUsers().subscribe({
      next: data => this.users = data,
      error: err => console.error(err),
      complete: () => console.log('Terminé')
    });
    Exemple complet - Service HTTP :
    // ===== api.service.ts =====
    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { Observable } from 'rxjs';
    
    @Injectable({ providedIn: 'root' })
    export class ApiService {
      private apiUrl = 'https://jsonplaceholder.typicode.com/users';
    
      constructor(private http: HttpClient) {}
    
      getUsers(): Observable<any[]> {
        return this.http.get<any[]>(this.apiUrl);
      }
    
      createUser(user: any): Observable<any> {
        return this.http.post<any>(this.apiUrl, user);
      }
    }
    
    // ===== app.component.ts =====
    import { Component, OnInit } from '@angular/core';
    import { ApiService } from './api.service';
    
    @Component({
      selector: 'app-root',
      template: `
        <ul>
          <li *ngFor="let user of users">{{ user.name }}</li>
        </ul>
        <button (click)="addUser()">Ajouter</button>
      `
    })
    export class AppComponent implements OnInit {
      users: any[] = [];
    
      constructor(private api: ApiService) {}
    
      ngOnInit() {
        this.loadUsers();
      }
    
      loadUsers() {
        this.api.getUsers().subscribe({
          next: (data) => this.users = data,
          error: (err) => console.error('Erreur API:', err)
        });
      }
    
      addUser() {
        this.api.createUser({ name: 'Nouveau' }).subscribe(() => {
          console.log('Utilisateur créé');
        });
      }
    }

    Observable vs Async Pipe

    Méthode Code Avantage/Inconvénient
    Manual Subscribe
    users: User[] = [];
    sub: Subscription;
    
    ngOnInit() {
      this.sub = this.api.getUsers().subscribe(
        data => this.users = data
      );
    }
    
    ngOnDestroy() {
      this.sub.unsubscribe(); // Obligatoire !
    }
    Contrôle total, mais unsubscribe manuel obligatoire
    Async Pipe
    // Component
    users$ = this.api.getUsers();
    
    // Template
    <li *ngFor="let user of users$ | async">{{ user.name }}</li>
    
    // Ou avec alias
    <div *ngIf="users$ | async as users">
      {{ users.length }} utilisateurs
    </div>
    Auto-subscribe/unsubscribe, code plus propre
    Comparaison - Manual vs Async Pipe :
    // ❌ MÉTHODE 1 : Manuel (Risque de fuite mémoire)
    export class UsersComponent implements OnInit, OnDestroy {
      users: User[] = [];
      private sub?: Subscription;
    
      ngOnInit() {
        this.sub = this.http.get<User[]>('/api/users').subscribe(data => {
          this.users = data; // Stockage manuel
        });
      }
    
      ngOnDestroy() {
        this.sub?.unsubscribe(); // Nettoyage manuel OBLIGATOIRE
      }
    }
    
    // ✅ MÉTHODE 2 : Async Pipe (Recommandé)
    export class UsersComponent {
      // Déclaration simple de l'Observable
      users$ = this.http.get<User[]>('/api/users');
      
      constructor(private http: HttpClient) {}
    }
    
    // Template (users$ | async)
    <ul>
      <li *ngFor="let user of users$ | async">
        {{ user.name }}
      </li>
    </ul>
    
    // Avantage Async Pipe :
    // 1. Subscribe automatique à l'initialisation
    // 2. Unsubscribe automatique à la destruction
    // 3. Met à jour la vue dès que les données arrivent
    // 4. Pas de variable intermédiaire nécessaire

    Pipes (Filtres)

    Pipe Description Exemple
    date Formater dates {{ today | date:'dd/MM/yyyy' }} → 13/03/2026
    uppercase Majuscules {{ 'hello' | uppercase }} → HELLO
    lowercase Minuscules {{ 'HELLO' | lowercase }} → hello
    currency Format monétaire {{ price | currency:'EUR' }} → €100.00
    decimal Nombre formaté {{ 3.14159 | number:'1.2-2' }} → 3.14
    percent Pourcentage {{ 0.25 | percent }} → 25%
    json Debug (affiche objet) {{ user | json }}
    async Subscribe auto Observable/Promise {{ users$ | async }}
    Exemple - Chaînage de Pipes :
    // Component
    today = new Date();
    price = 99.99;
    user = { name: 'Alice', role: 'admin' };
    
    // Template
    <p>Date: {{ today | date:'fullDate' }}</p>
    <p>Date courte: {{ today | date:'dd/MM/yy' | uppercase }}</p>
    <p>Prix: {{ price | currency:'EUR':'symbol':'1.2-2' }}</p>
    <p>User: {{ user | json }}</p>
    
    // Résultat :
    // Date: vendredi 13 mars 2026
    // Date courte: 13/03/26
    // Prix: €99.99
    // User: { "name": "Alice", "role": "admin" }

    Signals (Angular 16+)

    Concept Code Exemple Description
    signal()
    const count = signal(0);
    count.set(5);
    count.update(v => v + 1);
    console.log(count()); // 6
    Valeur réactive mutable
    computed()
    const double = computed(() => count() * 2);
    Signal dérivé (lazy evaluation)
    effect()
    effect(() => {
      console.log('Count:', count());
    });
    Effet de bord quand signal change
    Exemple - Signals complet :
    import { Component, signal, computed, effect } from '@angular/core';
    
    @Component({
      selector: 'app-counter',
      template: `
        <p>Compteur: {{ count() }}</p>
        <p>Double: {{ double() }}</p>
        <button (click)="increment()">+</button>
      `
    })
    export class CounterComponent {
      // 1. Signal (état réactif)
      count = signal(0);
    
      // 2. Computed (valeur dérivée automatique)
      double = computed(() => this.count() * 2);
    
      constructor() {
        // 3. Effect (réaction aux changements)
        effect(() => {
          console.log(`Le compteur a changé: ${this.count()}`);
        });
      }
    
      increment() {
        this.count.update(v => v + 1);
      }
    }
    
    // Flow :
    // 1. Click -> count devient 1
    // 2. double se met à jour automatiquement -> 2
    // 3. effect() s'exécute -> console: "Le compteur a changé: 1"

    Formulaires

    Type Description Code Exemple
    Template-driven Simple, logique dans template
    <form #form="ngForm" (ngSubmit)="onSubmit(form)">
      <input name="name" [(ngModel)]="user.name" required>
      <button [disabled]="!form.valid">Submit</button>
    </form>
    Reactive Forms Complexe, logique dans component
    // Component
    form = new FormGroup({
      name: new FormControl('', Validators.required),
      email: new FormControl('', [Validators.required, Validators.email])
    });
    
    // Template
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <input formControlName="name">
    </form>
    Exemple - Reactive Forms complet :
    // ===== user-form.component.ts =====
    import { Component } from '@angular/core';
    import { FormGroup, FormControl, Validators } from '@angular/forms';
    
    @Component({
      selector: 'app-user-form',
      template: `
        <form [formGroup]="userForm" (ngSubmit)="onSubmit()">
          
          <!-- Champ Nom -->
          <label>Nom:
            <input type="text" formControlName="name">
          </label>
          <p *ngIf="userForm.get('name')?.invalid && userForm.get('name')?.touched">
            Le nom est requis.
          </p>
    
          <!-- Champ Email -->
          <label>Email:
            <input type="email" formControlName="email">
          </label>
          <p *ngIf="userForm.get('email')?.errors?.['email']">
            Email invalide.
          </p>
    
          <button type="submit" [disabled]="userForm.invalid">Envoyer</button>
        </form>
    
        <pre>{{ userForm.value | json }}</pre>
      `
    })
    export class UserFormComponent {
      userForm = new FormGroup({
        name: new FormControl('', [Validators.required, Validators.minLength(3)]),
        email: new FormControl('', [Validators.required, Validators.email])
      });
    
      onSubmit() {
        if (this.userForm.valid) {
          console.log('Form Submitted:', this.userForm.value);
          // Résultat : { name: "Alice", email: "alice@test.com" }
        }
      }
    }
    ⚠️ Points importants :
    1. Toujours faire unsubscribe() dans ngOnDestroy() pour éviter les fuites mémoire (sauf avec async pipe) !
    2. providedIn: 'root' (minuscule) pour singleton global
    3. Wildcard ** TOUJOURS en dernier dans les routes !
    4. trackBy ou track obligatoire pour les boucles (optimisation)
    5. FormsModule requis pour [(ngModel)]

    🍃 Spring Boot

    Définition : Framework Java permettant de créer rapidement des applications autonomes avec auto-configuration et serveur embarqué (Tomcat par défaut).

    Annotations Essentielles - Code Exemples

    Annotation Rôle Code Exemple
    @SpringBootApplication Point d'entrée principal (combine @Configuration, @EnableAutoConfiguration, @ComponentScan)
    @SpringBootApplication
    public class MonApplication {
      public static void main(String[] args) {
        SpringApplication.run(MonApplication.class, args);
      }
    }
    @RestController Endpoints API retournant JSON (combine @Controller + @ResponseBody)
    @RestController
    @RequestMapping("/api/users")
    public class UserController {
      @GetMapping
      public List<User> getUsers() {
        return userService.findAll();
      }
    }
    @Controller Retourne des vues HTML (templates Thymeleaf/JSP)
    @Controller
    public class PageController {
      @GetMapping("/home")
      public String home(Model model) {
        model.addAttribute("msg", "Hello");
        return "home"; // home.html
      }
    }
    @Service Logique métier (couche service)
    @Service
    public class UserService {
      @Autowired
      private UserRepository repo;
    
      public User findById(Long id) {
        return repo.findById(id).orElse(null);
      }
    }
    @Repository Accès aux données (CRUD) - traduit les exceptions SQL en Spring DataAccessException
    @Repository
    public interface UserRepository extends JpaRepository<User, Long> {
      List<User> findByName(String name);
    }
    @Entity Classe = table BDD (JPA)
    @Entity
    @Table(name = "users")
    public class User {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;
      private String name;
    }
    @Autowired Injection automatique de dépendances (par constructeur recommandé)
    // Injection par constructeur (recommandé)
    @Service
    public class UserService {
      private final UserRepository repo;
    
      @Autowired
      public UserService(UserRepository repo) {
        this.repo = repo;
      }
    }
    @Transactional Gestion transactionnelle (rollback si échec)
    @Transactional
    public void transferMoney(Long from, Long to, Double amount) {
      accountRepo.debit(from, amount);
      accountRepo.credit(to, amount);
      // Rollback automatique si exception
    }
    Exemple complet - Structure d'une application :
    // 1. ENTITY (Modèle de données)
    @Entity
    @Table(name = "users")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        
        @Column(nullable = false)
        private String name;
        
        // Getters, Setters, Constructeurs
    }
    
    // 2. REPOSITORY (Accès données)
    @Repository
    public interface UserRepository extends JpaRepository<User, Long> {
        // Requête dérivée automatiquement
        Optional<User> findByEmail(String email);
    }
    
    // 3. SERVICE (Logique métier)
    @Service
    public class UserService {
        private final UserRepository userRepository;
    
        public UserService(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    
        public List<User> getAllUsers() {
            return userRepository.findAll();
        }
        
        public User createUser(User user) {
            return userRepository.save(user);
        }
    }
    
    // 4. CONTROLLER (API REST)
    @RestController
    @RequestMapping("/api/users")
    public class UserController {
        private final UserService userService;
    
        public UserController(UserService userService) {
            this.userService = userService;
        }
    
        @GetMapping
        public List<User> getUsers() {
            return userService.getAllUsers();
        }
    }
    
    // Résultat (JSON retourné par GET /api/users) :
    // [
    //   { "id": 1, "name": "Alice" },
    //   { "id": 2, "name": "Bob" }
    // ]

    Annotations Web - Mapping HTTP

    Annotation HTTP Method Code Exemple
    @GetMapping GET
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
      return service.findById(id);
    }
    @PostMapping POST
    @PostMapping
    public User createUser(@RequestBody User user) {
      return service.save(user);
    }
    @PutMapping PUT (idempotent)
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
      return service.update(id, user);
    }
    @DeleteMapping DELETE
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
      service.delete(id);
    }
    @PatchMapping PATCH (partiel)
    @PatchMapping("/{id}")
    public User patchUser(@PathVariable Long id, @RequestBody Map<String, Object> updates) {
      return service.partialUpdate(id, updates);
    }
    Exemple - CRUD Complet avec Endpoints :
    @RestController
    @RequestMapping("/api/products")
    public class ProductController {
    
        @Autowired
        private ProductService service;
    
        // GET /api/products
        @GetMapping
        public List<Product> getAll() {
            return service.findAll();
        }
    
        // GET /api/products/1
        @GetMapping("/{id}")
        public ResponseEntity<Product> getById(@PathVariable Long id) {
            return service.findById(id)
                    .map(ResponseEntity::ok)
                    .orElse(ResponseEntity.notFound().build());
        }
    
        // POST /api/products
        @PostMapping
        public Product create(@RequestBody Product product) {
            return service.save(product);
        }
    
        // PUT /api/products/1
        @PutMapping("/{id}")
        public Product update(@PathVariable Long id, @RequestBody Product product) {
            product.setId(id);
            return service.save(product);
        }
    
        // DELETE /api/products/1
        @DeleteMapping("/{id}")
        public ResponseEntity<Void> delete(@PathVariable Long id) {
            service.delete(id);
            return ResponseEntity.noContent().build();
        }
    }
    
    // Tests :
    // POST /api/products body: {"name":"Laptop", "price":999.99}
    // Résultat : 201 Created, {"id": 1, "name": "Laptop", "price": 999.99}
    
    // GET /api/products/999 (inexistant)
    // Résultat : 404 Not Found

    Paramètres de Requête

    Annotation Usage Code Exemple
    @PathVariable Variable dans l'URL /users/{id}
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) { ... }
    
    // URL: /users/123
    @RequestParam Paramètre de requête ?key=value
    @GetMapping("/users")
    public List<User> search(@RequestParam String name) { ... }
    
    // URL: /users?name=John
    @RequestBody Corps de la requête (JSON → Objet)
    @PostMapping("/users")
    public User create(@RequestBody User user) { ... }
    
    // JSON: {"name": "John", "email": "john@test.com"}
    @RequestHeader Valeur d'un header HTTP
    @GetMapping("/header")
    public String getHeader(@RequestHeader("X-Api-Key") String apiKey) { ... }
    Exemple - Combinaison des paramètres :
    @RestController
    @RequestMapping("/api")
    public class QueryController {
    
        // Combinaison PathVariable + RequestParam
        // GET /api/users/1/posts?published=true
        @GetMapping("/users/{userId}/posts")
        public List<Post> getUserPosts(
                @PathVariable Long userId,
                @RequestParam(required = false, defaultValue = "false") boolean published
        ) {
            return postService.findByUserAndStatus(userId, published);
        }
    
        // RequestParam avec valeur par défaut
        // GET /api/articles?page=2
        @GetMapping("/articles")
        public Page<Article> getArticles(
                @RequestParam(defaultValue = "0") int page,
                @RequestParam(defaultValue = "10") int size
        ) {
            return articleService.findAll(page, size);
        }
    }
    
    // Test : GET /api/articles?page=1&size=5
    // Résultat : Page 1 avec 5 éléments par page

    Architecture Couches

    Couche Rôle Code Exemple
    Controller Reçoit requêtes HTTP, valide entrées, appelle service
    @RestController
    public class UserController {
      @Autowired
      private UserService service;
    
      @GetMapping("/users/{id}")
      public ResponseEntity<User> getUser(@PathVariable Long id) {
        return ResponseEntity.ok(service.findById(id));
      }
    }
    Service Logique métier, transactions, règles métier
    @Service
    @Transactional
    public class UserService {
      public User createUser(UserDTO dto) {
        // Validation métier
        if (emailExists(dto.getEmail())) {
          throw new RuntimeException("Email existe déjà");
        }
        return userRepo.save(new User(dto));
      }
    }
    Repository Accès données, requêtes SQL/JPQL
    @Repository
    public interface UserRepository extends JpaRepository<User, Long> {
      @Query("SELECT u FROM User u WHERE u.email = :email")
      Optional<User> findByEmail(@Param("email") String email);
    }
    Entity/Model Représentation BDD (tables)
    @Entity
    public class User {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;
      private String email;
      private String password;
    }
    Flux de données Architecture 3-Tiers :
    Client (React/Angular/Mobile)
           |
           |  HTTP Request (JSON)
           v
    +------------------+
    |    Controller    |  @RestController
    |  (Validation)    |  Gère les endpoints
    +--------+---------+
             |
             |  Appel méthode Java
             v
    +------------------+
    |     Service      |  @Service
    | (Logique Métier) |  Transactions, Règles
    +--------+---------+
             |
             |  Appel JPA
             v
    +------------------+
    |    Repository    |  @Repository / JpaRepository
    |  (Accès Données) |  Génère SQL
    +--------+---------+
             |
             |  JDBC
             v
    +------------------+
    |    Database      |  MySQL / PostgreSQL
    +------------------+

    Méthodes HTTP - Comparaison

    Méthode Action Idempotent Exemple
    GET Récupérer des données ✅ Oui GET /users/123 → Récupère user 123
    POST Créer une ressource ❌ Non POST /users + body → Crée nouveau user
    PUT Mettre à jour complet (remplace tout) ✅ Oui PUT /users/123 + body complet → Remplace user 123
    PATCH Mettre à jour partiel (champs spécifiques) ❌ Non PATCH /users/123 + {email: "new@test.com"}
    DELETE Supprimer une ressource ✅ Oui DELETE /users/123 → Supprime user 123
    Exemple - Différence PUT vs PATCH :
    // Ressource existante :
    // { "id": 1, "name": "Alice", "email": "alice@old.com", "role": "user" }
    
    // 1. PUT /users/1 (Remplacement COMPLET)
    // Body envoyé :
    {
      "name": "Alice Updated",
      "email": "alice@new.com",
      "role": "admin"
    }
    // Résultat : Tout est remplacé. Si on oublie "role", il devient null.
    
    // 2. PATCH /users/1 (Mise à jour PARTIELLE)
    // Body envoyé :
    {
      "email": "alice@new.com"
    }
    // Résultat : Seul l'email change.
    // { "id": 1, "name": "Alice", "email": "alice@new.com", "role": "user" }

    JPA - Relations

    Annotation Relation Code Exemple
    @OneToOne Un-à-un (User ↔ Profile)
    @Entity
    public class User {
      @OneToOne(cascade = CascadeType.ALL)
      @JoinColumn(name = "profile_id")
      private Profile profile;
    }
    @OneToMany Un-à-plusieurs (côté "un" - User → Orders)
    @Entity
    public class User {
      @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
      private List<Order> orders;
    }
    @ManyToOne Plusieurs-à-un (côté "plusieurs", propriétaire FK)
    @Entity
    public class Order {
      @ManyToOne
      @JoinColumn(name = "user_id")
      private User user;
    }
    @ManyToMany Plusieurs-à-plusieurs (User ↔ Roles)
    @Entity
    public class User {
      @ManyToMany
      @JoinTable(
        name = "user_roles",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
      )
      private Set<Role> roles;
    }
    Exemple - Relation OneToMany bidirectionnelle :
    // ===== CÔTÉ "UN" (Parent) =====
    @Entity
    public class Category {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        
        private String name;
    
        @OneToMany(mappedBy = "category", cascade = CascadeType.ALL)
        private List<Product> products = new ArrayList<>();
        
        // Méthode helper pour synchroniser
        public void addProduct(Product product) {
            products.add(product);
            product.setCategory(this);
        }
    }
    
    // ===== CÔTÉ "PLUSIEURS" (Enfant - Propriétaire de la clé étrangère) =====
    @Entity
    public class Product {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        
        private String name;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "category_id") // Colonne FK dans table product
        private Category category;
    }
    
    // Utilisation :
    Category electronics = new Category();
    electronics.setName("Electronics");
    
    Product laptop = new Product();
    laptop.setName("Laptop");
    
    electronics.addProduct(laptop); // Synchronise les deux côtés
    categoryRepo.save(electronics); // Sauvegarde les deux grâce à CASCADE.ALL
    
    // Tables SQL créées :
    // categories (id, name)
    // products (id, name, category_id)

    FetchType - Comparaison

    Type Comportement Code Exemple Usage
    LAZY Chargement différé (à la demande) - optimisation mémoire @OneToMany(fetch = FetchType.LAZY) Par défaut pour @OneToMany et @ManyToMany. Données chargées uniquement quand accédées.
    EAGER Chargement immédiat (tout en une requête) - consommateur de ressources @ManyToOne(fetch = FetchType.EAGER) Par défaut pour @ManyToOne et @OneToOne. Données chargées avec l'entité parent.
    Exemple - Problème N+1 et LAZY Loading :
    // Configuration : @OneToMany(fetch = FetchType.LAZY)
    @Entity
    public class Author {
        @OneToMany(mappedBy = "author")
        private List<Book> books;
    }
    
    // === SCÉNARIO LAZY (Recommandé) ===
    Author author = authorRepo.findById(1L);
    // SQL exécuté : SELECT * FROM authors WHERE id = 1
    
    // Les livres ne sont PAS encore chargés
    author.getBooks(); // Déclenche UNE requête supplémentaire
    // SQL exécuté : SELECT * FROM books WHERE author_id = 1
    
    // === PROBLÈME AVEC EAGER ===
    // Si on a 100 auteurs
    List<Author> authors = authorRepo.findAll();
    // SQL : 1 requête pour les auteurs
    // + 100 requêtes pour les livres de chaque auteur (N+1 problem) !

    Validation - Annotations

    Annotation Description Code Exemple
    @Valid Déclenche la validation de l'objet
    @PostMapping
    public User create(@Valid @RequestBody User user) { ... }
    @NotNull Valeur non null (mais vide autorisé) @NotNull private String name;
    @NotEmpty Non null ET non vide (String, Collection) @NotEmpty private List<String> items;
    @NotBlank String non null, non vide, non espaces uniquement @NotBlank private String email;
    @Size Taille min/max (String, Collection) @Size(min=2, max=50) private String name;
    @Min / @Max Valeurs numériques min/max @Min(0) @Max(100) private int age;
    @Email Format email valide @Email private String email;
    @Pattern Regex personnalisée @Pattern(regexp="[A-Z]{2}") private String code;
    Exemple complet - Validation DTO :
    public class UserDTO {
        
        @NotBlank(message = "Le nom est obligatoire")
        @Size(min = 2, max = 50, message = "Le nom doit contenir entre 2 et 50 caractères")
        private String name;
    
        @Email(message = "Email invalide")
        @NotBlank
        private String email;
    
        @Min(value = 18, message = "Vous devez avoir au moins 18 ans")
        private int age;
    }
    
    // Controller
    @PostMapping("/users")
    public ResponseEntity<?> createUser(@Valid @RequestBody UserDTO userDTO, BindingResult result) {
        if (result.hasErrors()) {
            // Retourner les erreurs
            return ResponseEntity.badRequest().body(result.getAllErrors());
        }
        return ResponseEntity.ok(userService.create(userDTO));
    }
    
    // Test avec JSON invalide :
    // { "name": "", "email": "bad-email", "age": 10 }
    
    // Résultat (400 Bad Request) :
    // [
    //   {"field": "name", "message": "Le nom est obligatoire"},
    //   {"field": "email", "message": "Email invalide"},
    //   {"field": "age", "message": "Vous devez avoir au moins 18 ans"}
    // ]

    Gestion des Exceptions

    Annotation Rôle Code Exemple
    @ControllerAdvice Classe globale pour gérer les exceptions de tous les controllers
    @ControllerAdvice
    public class GlobalExceptionHandler {
    
      @ExceptionHandler(UserNotFoundException.class)
      public ResponseEntity<String> handleNotFound(UserNotFoundException ex) {
        return ResponseEntity.status(404).body(ex.getMessage());
      }
    
      @ExceptionHandler(Exception.class)
      public ResponseEntity<String> handleGeneric(Exception ex) {
        return ResponseEntity.status(500).body("Erreur interne");
      }
    }
    @ExceptionHandler Méthode gérant une exception spécifique
    // Dans @ControllerAdvice ou Controller
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegal(IllegalArgumentException ex) {
      return ResponseEntity.badRequest().body(ex.getMessage());
    }
    Exemple complet - Gestion globale des erreurs :
    // 1. Exception personnalisée
    public class ResourceNotFoundException extends RuntimeException {
        public ResourceNotFoundException(String message) {
            super(message);
        }
    }
    
    // 2. Gestionnaire global
    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(ResourceNotFoundException.class)
        public ResponseEntity<Map<String, String>> handleNotFound(ResourceNotFoundException ex) {
            Map<String, String> error = new HashMap<>();
            error.put("error", "Not Found");
            error.put("message", ex.getMessage());
            error.put("timestamp", LocalDateTime.now().toString());
            return ResponseEntity.status(404).body(error);
        }
    
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public ResponseEntity<Map<String, String>> handleValidation(MethodArgumentNotValidException ex) {
            Map<String, String> errors = new HashMap<>();
            ex.getBindingResult().getFieldErrors().forEach(error -> 
                errors.put(error.getField(), error.getDefaultMessage())
            );
            return ResponseEntity.badRequest().body(errors);
        }
    }
    
    // 3. Utilisation dans le Service
    public User getUserById(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id));
    }
    
    // Résultat si user introuvable :
    // HTTP 404
    // {
    //   "error": "Not Found",
    //   "message": "User not found with id: 999",
    //   "timestamp": "2024-03-13T10:30:00"
    // }

    Configuration - application.properties

    Propriété Description Exemple
    server.port Port du serveur embarqué server.port=8081
    spring.application.name Nom de l'application spring.application.name=mon-app
    spring.datasource.url URL de connexion BDD spring.datasource.url=jdbc:mysql://localhost:3306/db
    spring.datasource.username Utilisateur BDD spring.datasource.username=root
    spring.datasource.password Mot de passe BDD spring.datasource.password=secret
    spring.jpa.hibernate.ddl-auto Stratégie génération schéma spring.jpa.hibernate.ddl-auto=update (create, none)
    @Value Injecter valeur propriété
    @Value("${server.port}")
    private String port;
    Exemple - Fichier application.properties complet :
    # ===== SERVER =====
    server.port=8080
    
    # ===== DATABASE =====
    spring.datasource.url=jdbc:mysql://localhost:3306/ma_base
    spring.datasource.username=root
    spring.datasource.password=secret
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    
    # ===== JPA / HIBERNATE =====
    # Affiche les requêtes SQL dans la console
    spring.jpa.show-sql=true
    # Formate le SQL
    spring.jpa.properties.hibernate.format_sql=true
    # Met à jour le schéma automatiquement (ATTENTION en prod !)
    spring.jpa.hibernate.ddl-auto=update
    
    # ===== LOGGING =====
    logging.level.org.hibernate.SQL=debug
    logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace
    
    # ===== CUSTOM =====
    app.jwt.secret=monSecretTresLong
    app.pagination.default-size=20

    JpaRepository - Méthodes par défaut

    Méthode Description Exemple
    save() Créer ou mettre à jour une entité User saved = userRepo.save(user);
    findById() Trouver par ID (retourne Optional) Optional<User> user = userRepo.findById(1L);
    findAll() Récupérer toutes les entités List<User> users = userRepo.findAll();
    deleteById() Supprimer par ID userRepo.deleteById(1L);
    count() Compter les entités long total = userRepo.count();
    existsById() Vérifier existence par ID boolean exists = userRepo.existsById(1L);
    Exemple - Requêtes dérivées (Derived Queries) :
    @Repository
    public interface UserRepository extends JpaRepository<User, Long> {
    
        // Spring génère le SQL automatiquement selon le nom de la méthode !
        
        // SELECT * FROM users WHERE email = ?
        Optional<User> findByEmail(String email);
    
        // SELECT * FROM users WHERE name = ? AND active = true
        List<User> findByNameAndActiveTrue(String name);
    
        // SELECT * FROM users WHERE age > ? ORDER BY name ASC
        List<User> findByAgeGreaterThanOrderByAgeAsc(int age);
    
        // SELECT * FROM users WHERE name LIKE %?%
        List<User> findByNameContaining(String keyword);
    
        // SELECT COUNT(*) FROM users WHERE active = true
        long countByActiveTrue();
    
        // DELETE FROM users WHERE active = false
        void deleteByActiveFalse();
        
        // @Query personnalisée (JPQL)
        @Query("SELECT u FROM User u WHERE u.email LIKE %:domain")
        List<User> findByEmailDomain(@Param("domain") String domain);
        
        // Requête native SQL
        @Query(value = "SELECT * FROM users u WHERE u.role = :role", nativeQuery = true)
        List<User> findByRoleNative(@Param("role") String role);
    }
    
    // Utilisation :
    List<User> admins = userRepo.findByRoleNative("admin");
    FetchType :
    LAZY : chargement différé (optimisation mémoire) - données chargées uniquement quand accédées
    EAGER : chargement immédiat (consommateur de ressources) - données chargées avec l'entité parent

    🚀 Laravel

    Définition : Framework PHP MVC basé sur Symfony offrant syntaxe élégante et outils intégrés. Eloquent ORM Active Record, Blade templating, Artisan CLI.

    Commandes Artisan Essentielles

    Commande Action Exemple / Options
    php artisan serve Lancer serveur dev (port 8000) php artisan serve --port=8080
    php artisan make:controller Générer contrôleur php artisan make:controller UserController --resource (7 méthodes REST)
    php artisan make:model Créer modèle Eloquent php artisan make:model User -mfsc (Model + Migration + Factory + Seeder + Controller)
    php artisan migrate Exécuter migrations php artisan migrate --force (production)
    php artisan migrate:fresh --seed Réinitialiser BDD + seeders php artisan migrate:fresh --seed (DROP ALL + recrée + données test)
    php artisan db:seed Exécuter seeders php artisan db:seed --class=UserSeeder
    php artisan tinker Console interactive REPL User::find(1) ou $user = new User(['name' => 'John'])
    php artisan route:list Afficher toutes les routes php artisan route:list --path=api
    php artisan cache:clear Vider cache application php artisan config:cache (cache config production)
    Exemple - Workflow complet de création :
    # 1. Créer un Model avec Migration et Controller Resource
    php artisan make:model Product -mr
    
    # Résultat fichiers créés :
    # app/Models/Product.php
    # database/migrations/2024_03_13_create_products_table.php
    # app/Http/Controllers/ProductController.php
    
    # 2. Modifier la migration (database/migrations/...)
    Schema::create('products', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->decimal('price', 8, 2);
        $table->timestamps();
    });
    
    # 3. Exécuter la migration
    php artisan migrate
    # Résultat : Table 'products' créée en BDD

    Eloquent ORM - Méthodes Clés

    Méthode Action Retourne si non trouvé Code Exemple
    all() Tous les enregistrements Collection vide $users = User::all();
    find($id) Recherche par ID null $user = User::find(1);
    findOrFail($id) Recherche par ID Exception 404 $user = User::findOrFail(1); // Auto 404 si inexistant
    first() Premier résultat null $user = User::where('active', 1)->first();
    firstOrFail() Premier résultat Exception 404 $user = User::where('email', $email)->firstOrFail();
    firstOrCreate($attrs) Recherche ou crée Instance (existante ou nouvelle) User::firstOrCreate(['email' => 'test@test.com'], ['name' => 'John']);
    create($data) Créer (respecte $fillable) Instance créée $user = User::create(['name' => 'John', 'email' => 'john@test.com']);
    update($data) Met à jour l'instance bool $user->update(['name' => 'Jane']);
    save() Persiste l'instance bool $user->name = 'Jane'; $user->save();
    delete() Supprime l'instance bool $user->delete();
    destroy($id) Supprime par ID int (nb supprimés) User::destroy(1); ou User::destroy([1, 2, 3]);
    where($col, $op, $val) Condition WHERE Query Builder User::where('age', '>', 18)->get();
    paginate($n) Pagination LengthAwarePaginator $users = User::paginate(10); // 10 par page
    with($relation) Eager loading (évite N+1) Query Builder $users = User::with('posts')->get();
    Exemple complet - CRUD avec Eloquent :
    // ===== CREATE =====
    // Méthode 1: Instanciation + save()
     $user = new User();
     $user->name = 'Alice';
     $user->email = 'alice@test.com';
     $user->save(); // INSERT INTO users...
    
    // Méthode 2: Mass Assignment (rapide)
     $user = User::create([
        'name' => 'Bob',
        'email' => 'bob@test.com'
    ]); // Respecte $fillable dans le modèle
    
    // ===== READ =====
    // Récupérer tous
     $allUsers = User::all(); // Collection
    
    // Récupérer par ID
     $user = User::find(1); // Retourne User ou null
    
    // Récupérer avec conditions
     $activeUsers = User::where('active', true)
                       ->orderBy('name')
                       ->get(); // Collection
    
    // ===== UPDATE =====
     $user = User::find(1);
     $user->email = 'new-email@test.com';
     $user->save(); // UPDATE users SET email = ...
    
    // Mass update
    User::where('active', false)->update(['status' => 'archived']);
    
    // ===== DELETE =====
     $user = User::find(1);
     $user->delete(); // DELETE FROM users WHERE id = 1
    
    // Delete par ID
    User::destroy(2);       // Supprime user ID 2
    User::destroy([3, 4]);  // Supprime users ID 3 et 4
    Différences clés :
    find() = retourne null si inexistant | findOrFail() = lance exception 404 si inexistant
    create() = Eloquent avec événements (respecte $fillable) | insert() = SQL brut rapide (pas d'événements)
    save() = sur instance existante | update() = mass update sur query builder

    Relations Eloquent

    Méthode Relation Code Exemple
    hasOne() Un-à-un (détient FK)
    // User a un Profile
    public function profile() {
      return $this->hasOne(Profile::class);
    }
    belongsTo() Inverse one-to-one/many-to-one
    // Profile appartient à User
    public function user() {
      return $this->belongsTo(User::class);
    }
    hasMany() Un-à-plusieurs
    // User a plusieurs Posts
    public function posts() {
      return $this->hasMany(Post::class);
    }
    belongsToMany() Plusieurs-à-plusieurs (table pivot)
    // User a plusieurs Roles (table pivot role_user)
    public function roles() {
      return $this->belongsToMany(Role::class);
    }
    Exemple - Relation OneToMany et utilisation :
    // ===== Modèles =====
    
    // app/Models/User.php
    class User extends Model {
        public function posts() {
            return $this->hasMany(Post::class);
            // Laravel suppose que la table 'posts' a une colonne 'user_id'
        }
    }
    
    // app/Models/Post.php
    class Post extends Model {
        public function user() {
            return $this->belongsTo(User::class);
        }
    }
    
    // ===== Utilisation dans Controller =====
    
    // 1. Récupérer les posts d'un utilisateur
     $user = User::find(1);
     $posts = $user->posts; // Collection de posts
    
    // 2. Récupérer l'auteur d'un post
     $post = Post::find(1);
     $author = $post->user; // Instance de User
    
    // 3. Créer un post pour un user
     $user->posts()->create([
        'title' => 'Mon premier article',
        'content' => 'Contenu...'
    ]); // user_id est automatiquement défini
    
    // 4. Eager Loading (optimisation N+1)
    // Sans with() : 1 requête users + N requêtes posts
    // Avec with() : 2 requêtes seulement
     $users = User::with('posts')->get();
    
    foreach ($users as $user) {
        echo $user->posts->count(); // Pas de requête supplémentaire
    }

    Sécurité - Mass Assignment

    Propriété Rôle Code Exemple
    $fillable Whitelist : champs autorisés pour create/update
    protected $fillable = ['name', 'email', 'password'];
    // Seuls ces champs peuvent être assignés en masse
    $guarded Blacklist : champs interdits (inverse de fillable)
    protected $guarded = ['is_admin', 'role'];
    // Tous les champs SAUF ceux-ci sont assignables
    Exemple - Protection Mass Assignment :
    // Modèle User
    class User extends Model {
        // Sécurité : seul 'name' et 'email' sont assignables via create()
        protected $fillable = ['name', 'email'];
    }
    
    // Tentative d'attaque (Injection de rôle admin)
    User::create([
        'name' => 'Hacker',
        'email' => 'hack@test.com',
        'role' => 'admin' // ⚠️ IGNORÉ car pas dans $fillable
    ]);
    
    // Résultat en base :
    // { id: 2, name: "Hacker", email: "hack@test.com", role: "user" }
    // Le champ 'role' n'a pas été modifié.
    
    // Alternative : Tout autoriser (déconseillé en prod)
    // protected $guarded = []; // Vide = tout est fillable

    Routing

    Méthode Usage Code Exemple
    Route::get() Route GET Route::get('/users', [UserController::class, 'index']);
    Route::post() Route POST Route::post('/users', [UserController::class, 'store']);
    Route::put() Route PUT Route::put('/users/{id}', [UserController::class, 'update']);
    Route::delete() Route DELETE Route::delete('/users/{id}', [UserController::class, 'destroy']);
    Route::resource() 7 routes REST complètes Route::resource('users', UserController::class); // index, create, store, show, edit, update, destroy
    Route::apiResource() 5 routes API (sans create/edit) Route::apiResource('users', UserController::class); // index, store, show, update, destroy
    ->name() Nommer une route Route::get('/users', [UserController::class, 'index'])->name('users.index');
    ->middleware() Appliquer middleware Route::get('/admin', [AdminController::class, 'index'])->middleware('auth');
    Exemple - Routes API Resource :
    // routes/web.php ou routes/api.php
    
    use App\Http\Controllers\ProductController;
    
    // Une seule ligne pour 5 routes REST
    Route::apiResource('products', ProductController::class);
    
    // Equivalent à écrire manuellement :
    // Method    URI                     Action
    // GET       /products               index
    // POST      /products               store
    // GET       /products/{product}     show
    // PUT/PATCH /products/{product}     update
    // DELETE    /products/{product}     destroy
    
    // ===== ProductController.php (Généré avec --api) =====
    class ProductController extends Controller {
        public function index() {
            return Product::all();
        }
    
        public function store(Request $request) {
            return Product::create($request->all());
        }
    
        public function show(Product $product) {
            return $product; // Route Model Binding
        }
    
        public function update(Request $request, Product $product) {
            $product->update($request->all());
            return $product;
        }
    
        public function destroy(Product $product) {
            $product->delete();
            return response()->json(null, 204);
        }
    }

    Blade Directives

    Directive Usage Code Exemple
    @extends('layout') Hérite d'un template @extends('layouts.app')
    @section('nom') Définit une section @section('content') ... @endsection
    @yield('nom') Emplacement d'injection <main>@yield('content')</main> (dans layout)
    @include('vue') Inclut une sous-vue @include('partials.header')
    @if Condition
    @if($user->isAdmin())
      <p>Admin</p>
    @elseif($user->isEditor())
      <p>Editor</p>
    @else
      <p>User</p>
    @endif
    @foreach Boucle sur collection
    @foreach($users as $user)
      <p>{{ $user->name }}</p>
    @endforeach
    @forelse Boucle avec gestion cas vide
    @forelse($users as $user)
      <p>{{ $user->name }}</p>
    @empty
      <p>Aucun utilisateur</p>
    @endforelse
    @csrf Token protection CSRF <form>@csrf ...</form> // OBLIGATOIRE pour POST/PUT/DELETE
    @method('PUT') Spoofing méthode HTTP <form>@csrf @method('PUT') ...</form> // Pour simuler PUT dans form HTML
    @auth / @guest Vérification authentification
    @auth
      Bienvenue {{ Auth::user()->name }}
    @guest
      <a href="/login">Connexion</a>
    @endauth
    Exemple - Layout et Page Blade :
    // ===== resources/views/layouts/app.blade.php =====
    
    
    
        Mon Site - @yield('title')
    
    
        @include('partials.navbar') // Inclusion
        
        
    @yield('content') // Emplacement principal
    @stack('scripts') // Pile de scripts // ===== resources/views/users/index.blade.php ===== @extends('layouts.app') // Héritage @section('title', 'Liste des utilisateurs') @section('content') <h1>Utilisateurs</h1> @forelse($users as $user) <div class="card"> <h3>{{ $user->name }}</h3> <p>{{ $user->email }}</p> </div> @empty <p>Aucun utilisateur trouvé.</p> @endforelse @push('scripts') <script src="/js/users.js"></script> @endpush @endsection

    Validation - Règles Courantes

    Règle Description Exemple
    required Champ obligatoire 'name' => 'required'
    nullable Peut être null 'bio' => 'nullable|string'
    string Doit être une chaîne 'name' => 'required|string|max:255'
    email Format email valide 'email' => 'required|email|unique:users'
    unique:table,column Valeur unique en BDD 'email' => 'unique:users,email'
    exists:table,column Doit exister en BDD 'user_id' => 'exists:users,id'
    min:value Taille minimale 'password' => 'min:8'
    confirmed Doit avoir champ _confirmation 'password' => 'required|confirmed' // attend password_confirmation
    array Doit être un tableau 'tags' => 'array'
    Exemple - Validation dans Controller :
    // UserController.php
    public function store(Request $request) {
        // Validation automatique
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|min:8|confirmed',
        ]);
    
        // Si validation échoue : redirection automatique avec erreurs
        // Si succès : on continue
        
        $user = User::create($validated);
        
        return redirect()->route('users.show', $user);
    }
    
    // ===== Affichage des erreurs dans Blade =====
    @if ($errors->any())
        <div class="alert alert-danger">
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif
    
    // Exemple si email invalide envoyé :
    // Erreur : "The email must be a valid email address."

    Middleware

    Concept Description Code Exemple
    handle() Méthode principale du middleware
    public function handle($request, $next) {
      if (!Auth::check()) {
        return redirect('login');
      }
      return $next($request); // Passe au suivant
    }
    Route::middleware() Appliquer à une route Route::get('/admin', fn() => view('admin'))->middleware('auth');
    Middleware Groups Groupes prédéfinis web (sessions, CSRF) et api (stateless, throttle)
    Exemple - Middleware personnalisé :
    # 1. Création
    php artisan make:middleware CheckAdmin
    
    // app/Http/Middleware/CheckAdmin.php
    public function handle($request, Closure $next) {
        if (auth()->user()->role !== 'admin') {
            abort(403, 'Accès non autorisé');
        }
        return $next($request);
    }
    
    // 2. Enregistrement dans app/Http/Kernel.php
    protected $routeMiddleware = [
        'admin' => \App\Http\Middleware\CheckAdmin::class,
    ];
    
    // 3. Utilisation dans routes/web.php
    Route::get('/admin/dashboard', function () {
        return "Dashboard Admin";
    })->middleware(['auth', 'admin']); // Empilé : il faut être connecté ET admin
    
    // Résultat si user non-admin essaie d'accéder :
    // HTTP 403 Forbidden

    Configuration & Environnement

    Fichier/Fonction Usage Exemple
    .env Variables d'environnement APP_NAME=Laravel
    DB_DATABASE=laravel_db
    env('KEY', $default) Lire variable env $db = env('DB_DATABASE', 'forge');
    config('file.key') Lire configuration $timezone = config('app.timezone');

    Helpers Essentiels

    Helper Description Exemple
    route('name') Génère URL depuis nom de route <a href="{{ route('users.index') }}">Users</a>
    url('path') Génère URL absolue url('/images/logo.png')
    asset('path') URL vers fichier public <img src="{{ asset('images/logo.png') }}">
    storage_path('path') Chemin vers storage storage_path('app/public/file.txt')
    Hash::make() Hacher mot de passe (Bcrypt) $user->password = Hash::make('secret');
    Auth::user() Utilisateur connecté {{ Auth::user()->name }}
    Exemple - Fichier .env et utilisation :
    // .env (à la racine)
    APP_NAME="Mon Projet"
    APP_ENV=local
    APP_DEBUG=true
    
    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=ma_base
    DB_USERNAME=root
    DB_PASSWORD=secret
    
    // Utilisation dans config/database.php
    'mysql' => [
        'database' => env('DB_DATABASE', 'forge'),
        // Si .env absent, utilise 'forge' par défaut
    ],
    
    // Accès dans le code
     $appName = env('APP_NAME'); // "Mon Projet"
     $debug = config('app.debug'); // true
    
    // ⚠️ Ne jamais commiter le fichier .env sur Git !
    // Utiliser .env.example comme template
    ⚠️ Points clés pour QCM :
    1. find() vs findOrFail() : null vs Exception 404
    2. $fillable (whitelist) vs $guarded (blacklist) pour Mass Assignment
    3. Route::resource() = 7 routes (avec vues) vs Route::apiResource() = 5 routes (API uniquement)
    4. hasMany() = côté "un" de la relation | belongsTo() = côté "plusieurs" (détient FK)
    5. @csrf OBLIGATOIRE dans tous les formulaires POST/PUT/DELETE
    6. with() = eager loading (évite requêtes N+1)

    🌐 Réseaux

    Modèle OSI (7 couches)

    Couche Nom Fonction Exemples
    7 Application Services réseau pour applications HTTP, FTP, SMTP, DNS
    6 Présentation Encodage, compression, chiffrement SSL/TLS, JPEG, ASCII
    5 Session Gestion des dialogues NetBIOS, RPC
    4 Transport Fiabilité, séquencement, ports TCP, UDP
    3 Réseau Routage, adressage logique (IP) IP, ICMP, OSPF
    2 Liaison Adressage physique (MAC), switch Ethernet, PPP
    1 Physique Signaux, câbles, bits Câble RJ45, fibre, Hub

    Protocoles Essentiels

    Protocole Port Fonction
    HTTP / HTTPS 80 / 443 Web (chiffrement SSL/TLS pour HTTPS)
    FTP / SFTP 21 / 22 Transfert fichiers (SFTP sécurisé)
    SMTP / POP3 / IMAP 25 / 110 / 143 Email (envoi / réception)
    DNS 53 Résolution noms de domaine → IP
    DHCP 67/68 Attribution dynamique d'IP
    SSH 22 Connexion sécurisée distante

    TCP vs UDP

    TCP (6) UDP (17)
    Connexion fiable Non fiable
    Séquencement garanti Pas de séquencement
    Accusé de réception (ACK) Pas d'ACK
    Lent (overhead) Rapide (streaming)

    Matériel Réseau

    Matériel Couche OSI Fonction
    Hub 1 (Physique) Broadcast tout (ancien, non intelligent)
    Switch 2 (Liaison) Commutation par MAC (intelligent)
    Routeur 3 (Réseau) Routage par IP entre réseaux
    Firewall 3-7 Sécurité, filtrage paquets

    Commandes Réseau

    Commande Usage
    ping Tester connectivité
    ipconfig / ifconfig Configuration IP locale
    tracert / traceroute Chemin vers destination
    nslookup / dig Résolution DNS
    netstat Connexions actives et ports
    arp -a Table ARP (IP ↔ MAC)
    Adresses IP privées :
    Classe A : 10.0.0.0 - 10.255.255.255
    Classe B : 172.16.0.0 - 172.31.255.255
    Classe C : 192.168.0.0 - 192.168.255.255

    📦 Git & GitHub

    Mots clés : VCS (Version Control System), Décentralisé, Historique
    Git est un système de contrôle de version distribué. Il permet de suivre les modifications, revenir en arrière et collaborer.

    Les 3 Zones Git

    Zone Nom Description
    1. Working Directory Répertoire de travail Fichiers modifiés mais pas encore indexés
    2. Staging Area (Index) Zone de préparation Fichiers prêts à être commités (git add)
    3. Repository (.git) Dépôt local Historique complet des commits (git commit)

    Commandes Essentielles

    Commande Action Exemple
    git init Initialiser un dépôt Crée le dossier caché .git
    git clone url Copier un dépôt distant git clone https://github.com/user/repo.git
    git status Voir l'état des fichiers Rouge = non suivi, Vert = stagé
    git add fichier Ajouter au Staging git add . (ajoute tout)
    git commit -m "msg" Sauvegarder dans l'historique Crée un "snapshot" immuable
    git push Envoyer vers le distant git push origin main
    git pull Récupérer et fusionner git pull origin main
    git log Voir l'historique git log --oneline (compact)

    Gestion des Branches

    Mots clés : Branch, Head, Merge, Checkout
    Une branche est un pointeur mobile vers un commit.
    git branch feature-login  // Créer branche
    git checkout feature-login // Basculer dessus
    git checkout -b feature-x  // Créer + Basculer (raccourci)
    
    // Après travail sur la branche
    git checkout main          // Retour au tronc
    git merge feature-login    // Fusionner dans main

    Fichier .gitignore

    Usage : Ignorer des fichiers (ne jamais les tracker).
    # Contenu typique de .gitignore
    node_modules/    # Dossiers dépendances
    .env             # Fichiers secrets
    *.log            # Tous les fichiers .log
    build/           # Dossiers de build

    🐳 Docker & DevOps

    Définition : Docker est une plateforme de conteneurisation. Elle permet d'empaqueter une application avec toutes ses dépendances dans un "Conteneur" isolé, assurant ainsi qu'elle fonctionne de manière identique partout.

    Concepts Clés

    Concept Analogie Description
    Image Recette de cuisine / Classe Modèle en lecture seule (Bluepring). Contient l'OS, libs, code.
    Conteneur Plat cuisiné / Objet Instance en cours d'exécution d'une image. Isolé, léger.
    Dockerfile Mode d'emploi Fichier texte contenant les instructions pour construire une image.
    Volume Disque dur externe Stockage persistant. Les données survivent à la suppression du conteneur.

    Commandes Docker

    Commande Action
    docker build -t nom . Construire une image depuis Dockerfile
    docker run -d -p 80:80 nom Lancer un conteneur (-d: détaché, -p: port)
    docker ps Lister les conteneurs actifs
    docker stop id Arrêter un conteneur
    docker exec -it id bash Entrer dans le conteneur (shell interactif)

    Dockerfile - Instructions

    Exemple de Dockerfile standard :
    FROM node:18-alpine        # Image de base
    WORKDIR /app              # Dossier de travail
    COPY package*.json ./      # Copier fichiers dépendances
    RUN npm install           # Installer dépendances
    COPY . .                  # Copier tout le code
    EXPOSE 3000               # Exposer le port
    CMD ["npm", "start"]      # Commande au démarrage

    Docker Compose

    Mots clés : Multi-conteneurs, Orchestration local
    Outil pour définir et lancer des applications multi-conteneurs via un fichier YAML.
    # docker-compose.yml
    version: '3'
    services:
      web:
        image: nginx
        ports:
          - "8080:80"
      database:
        image: mysql
        environment:
          MYSQL_ROOT_PASSWORD: secret
    
    # Commande : docker-compose up -d

    🧮 Algorithmes & Complexité

    Objectif : Résoudre des problèmes de manière efficace. On mesure l'efficacité par la complexité temporelle (temps d'exécution) et spatiale (mémoire utilisée).

    Notation Big O (Complexité)

    Notation Nom Performance Exemple
    O(1) Constant ⭐⭐⭐ Excellent Accès à un élément d'un tableau par index arr[5]
    O(log n) Logarithmique ⭐⭐ Bon Recherche binaire (Binary Search)
    O(n) Linéaire ⭐ Moyen Parcourir une liste simple
    O(n log n) Linéarithmique ⭐ Acceptable Tri rapide (Quick Sort), Tri fusion (Merge Sort)
    O(n²) Quadratique ❌ Mauvais Boucles imbriquées, Tri à bulles
    Règle d'or : On considère toujours le pire cas (Worst Case). Si un algorithme est rapide parfois mais lent sur de grandes données, on note sa complexité maximale.

    Structures de Données

    Structure Description Complexité Accès
    Array (Tableau) Collection indexée, taille fixe O(1)
    Linked List (Liste Chaînée) Éléments liés par pointeurs O(n)
    Stack (Pile) LIFO (Last In First Out) Push/Pop: O(1)
    Queue (File) FIFO (First In First Out) Enqueue/Dequeue: O(1)
    Hash Table Paires clé-valeur (Objet/Map) Accès moyen: O(1)

    Algorithmes de Tri

    Algorithme Complexité Moyenne Principe
    Bubble Sort O(n²) Compare et échange les éléments adjacents. Lent.
    Quick Sort O(n log n) Choisit un pivot, divise le tableau, trie récursivement.
    Merge Sort O(n log n) Divise pour régner. Fusionne des sous-tableaux triés.

    Recherche

    1. Recherche Linéaire (Linear Search) : O(n)
    Parcourt chaque élément un par un.
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] === target) return i;
    }
    2. Recherche Binaire (Binary Search) : O(log n)
    Divise l'espace de recherche par 2 à chaque itération. Prérequis : Tableau trié.
    let start = 0, end = arr.length - 1;
    while (start <= end) {
        let mid = Math.floor((start + end) / 2);
        if (arr[mid] === target) return mid;
        else if (arr[mid] < target) start = mid + 1;
        else end = mid - 1;
    }

    © 2026 - Cours Essentiels pour Préparation Examen QCM

    Bonne chance pour vos examens ! 🎓

    📝 QCM DE RÉVISION GLOBALE

    Chaque section contient 20 questions couvrant les notions essentielles du cours.

    1. HTML & CSS

    1. Quelle balise est utilisée pour définir l'importance sémantique (forte) d'un texte ?

    Réponse : B (<strong> a une valeur sémantique, contrairement à <b>)

    2. Quelle est la différence principale entre une classe (.class) et un ID (#id) ?

    Réponse : C

    3. Quelle propriété CSS permet de centrer un élément flex horizontalement et verticalement ?

    Réponse : C

    4. Dans le Box Model standard (content-box), la propriété width inclut :

    Réponse : B

    5. Quel attribut HTML est obligatoire pour les images pour l'accessibilité ?

    Réponse : C

    6. Quelle balise HTML5 est utilisée pour le contenu principal unique d'une page ?

    Réponse : C

    7. Comment inclure un fichier CSS externe ?

    Réponse : B

    8. Quelle est la spécificité CSS la plus élevée ?

    Réponse : D (Inline style)

    9. Quelle propriété retire l'élément du flux normal sans réserver d'espace ?

    Réponse : A

    10. Quelle unité est relative à la taille de la racine (<html>) ?

    Réponse : C

    11. Quelle balise est utilisée pour une navigation principale ?

    Réponse : B

    12. Comment sélectionner tous les éléments <p> directs à l'intérieur d'un <div> ?

    Réponse : B

    13. Quelle balise crée une liste numérotée ?

    Réponse : C

    14. Quelle est la valeur par défaut de la propriété position ?

    Réponse : C

    15. Quelle balise est utilisée pour créer un champ de saisie dans un formulaire ?

    Réponse : A

    16. Quelle propriété Flexbox définit l'axe principal ?

    Réponse : B

    17. Quel attribut de formulaire lie un label à un champ input ?

    Réponse : C (l'input doit avoir l'id correspondant)

    18. Quelle valeur de display transforme un élément en conteneur Grid ?

    Réponse : C

    19. Quel attribut empêche un champ de formulaire d'être vide ?

    Réponse : B

    20. Quel meta tag est indispensable pour le Responsive Design ?

    Réponse : C

    2. JavaScript

    1. Quelle est la différence entre == et === ?

    Réponse : B

    2. Quelle méthode de tableau NE MODIFIE PAS l'original ?

    Réponse : C

    3. Quel est le résultat de typeof null ?

    Réponse : C (Bug historique JS)

    4. Comment déclarer une variable non réassignable ?

    Réponse : C

    5. Quelle méthode DOM retourne le premier élément correspondant à un sélecteur CSS ?

    Réponse : C

    6. Quelle méthode transforme un tableau en appliquant une fonction à chaque élément ?

    Réponse : C

    7. Quelle est la sortie de [1, 2, 3].pop() ?

    Réponse : B (pop retourne l'élément supprimé)

    8. Comment convertir une chaîne JSON en objet JS ?

    Réponse : B

    9. Quelle est la portée de var ?

    Réponse : B

    10. Quelle propriété compte le nombre de caractères d'une chaîne ?

    Réponse : B

    11. "Hello".indexOf("z") retourne ?

    Réponse : C

    12. Quelle méthode supprime le premier élément d'un tableau ?

    Réponse : A

    13. Quelle syntaxe est correcte pour une arrow function ?

    Réponse : B

    14. Que retourne document.getElementsByClassName("x") ?

    Réponse : C

    15. Comment ajouter un événement clic ?

    Réponse : D (Mais addEventListener est recommandé)

    16. Quelle méthode fusionne des tableaux ?

    Réponse : B

    17. Quelle est la différence entre innerHTML et textContent ?

    Réponse : B

    18. Quel opérateur permet d'assigner une valeur par défaut si la variable est null/undefined ?

    Réponse : C (Nullish coalescing)

    19. Que signifie AJAX ?

    Réponse : A

    20. "5" + 3 donne ?

    Réponse : B (Concaténation)

    3. SQL, NoSQL, UML & OOP

    1. Quelle commande SQL supprime complètement une table et ses données ?

    Réponse : B

    2. Quelle jointure retourne toutes les lignes de la table de gauche même sans correspondance ?

    Réponse : C

    3. Quelle est la différence entre WHERE et HAVING ?

    Réponse : B

    4. Quel type de NoSQL utilise des documents JSON/BSON ?

    Réponse : C

    5. En UML, quelle relation indique un cycle de vie dépendant (la partie meurt avec le tout) ?

    Réponse : C

    6. En OOP, quel concept cache l'état interne d'un objet ?

    Réponse : C

    7. Quel concept permet à une classe d'hériter des propriétés d'une autre ?

    Réponse : B

    8. Quelle requête SQL efface toutes les lignes d'une table sans journalisation (rapide) ?

    Réponse : C

    9. En UML, <> signifie :

    Réponse : B

    10. Quelle fonction SQL compte le nombre de lignes ?

    Réponse : B

    11. SQL est aux bases relationnelles ce que NoSQL est aux :

    Réponse : C

    12. Une interface en OOP est :

    Réponse : B

    13. Le polymorphisme permet :

    Réponse : B

    14. Quelle est la bonne syntaxe pour un tri décroissant ?

    Réponse : A

    15. Quel symbole représente l'agrégation en UML ?

    Réponse : B

    16. La clé primaire (PRIMARY KEY) :

    Réponse : B

    17. En NoSQL, le scaling horizontal signifie :

    Réponse : B

    18. Quel diagramme UML montre l'ordre des messages entre objets ?

    Réponse : C

    19. Une classe abstraite :

    Réponse : B

    20. Quel mot-clé SQL filtre un groupe après GROUP BY ?

    Réponse : C

    4. Langages (C, Java, Python, PHP)

    1. En C, pourquoi utilise-t-on & dans scanf("%d", &x) ?

    Réponse : B

    2. En Java, comment compare-t-on le contenu de deux String ?

    Réponse : B

    3. En Python, quelle structure est IMMUTABLE (non modifiable) ?

    Réponse : C

    4. En PHP, quelle fonction ajoute un élément à la fin d'un tableau ?

    Réponse : A

    5. Quel est le point d'entrée d'un programme C ?

    Réponse : C

    6. En Java, quelle méthode affiche une ligne dans la console ?

    Réponse : B

    7. En Python, comment insérer un élément à un index spécifique ?

    Réponse : B

    8. En PHP, comment déclare-t-on une variable ?

    Réponse : B

    9. En C, %d est le format pour :

    Réponse : C

    10. En Java, int est un type primitif, Integer est :

    Réponse : B

    11. En Python, "Hello".upper() retourne :

    Réponse : B

    12. En PHP, quelle fonction compte les éléments d'un tableau ?

    Réponse : B

    13. En C, strlen() retourne :

    Réponse : B

    14. En Java, ArrayList stocke :

    Réponse : B (Les types primitifs utilisent les wrappers comme Integer)

    15. En Python, quelle fonction lit l'entrée utilisateur ?

    Réponse : C

    16. En PHP, quelle instruction affiche du texte ?

    Réponse : D

    17. En C, la fonction strcpy sert à :

    Réponse : B

    18. En Java, String est :

    Réponse : B

    19. En Python, range(5) génère :

    Réponse : B

    20. En PHP, strpos() retourne :

    Réponse : A

    5. React.js

    1. React est une :

    Réponse : B

    2. Quel hook gère l'état local ?

    Réponse : B

    3. Les Props sont :

    Réponse : B

    4. Qu'est-ce que le Virtual DOM ?

    Réponse : B

    5. Quelle règle est vraie pour JSX ?

    Réponse : B

    6. Comment optimiser les listes en React ?

    Réponse : B

    7. useEffect(() => {}, []) s'exécute :

    Réponse : B

    8. Comment accéder directement à un élément DOM ?

    Réponse : B

    9. Comment déclencher une mise à jour du state ?

    Réponse : B

    10. Que retourne map() en JSX ?

    Réponse : B

    11. "Lifting State Up" signifie :

    Réponse : B

    12. Quelle syntaxe pour un événement clic ?

    Réponse : B

    13. React.memo est utilisé pour :

    Réponse : B

    14. useState est :

    Réponse : B

    15. Comment passer des données entre composants non liés ?

    Réponse : B

    16. Quel hook remplace componentDidMount, componentDidUpdate ?

    Réponse : B

    17. className en JSX remplace :

    Réponse : B

    18. Un composant contrôlé (controlled component) signifie :

    Réponse : B

    19. Où place-t-on les effets de bord (API calls) ?

    Réponse : B

    20. React est :

    Réponse : B

    6. Angular

    1. Angular utilise principalement quel langage ?

    Réponse : B

    2. Quelle syntaxe pour le Two-Way Binding ?

    Réponse : C ("Banana in a box")

    3. Quel décorateur crée un composant ?

    Réponse : B

    4. Quelle commande CLI crée un nouveau projet ?

    Réponse : A

    5. *ngIf est une directive :

    Réponse : B

    6. Le décorateur @Input() permet :

    Réponse : A

    7. Quel hook est appelé à l'initialisation du composant ?

    Réponse : B

    8. providedIn: 'root' dans un service signifie :

    Réponse : B

    9. Quelle syntaxe pour la liaison d'événement ?

    Réponse : B

    10. HttpClient retourne un :

    Réponse : B

    11. Quelle balise HTML utilise-t-on pour le routing ?

    Réponse : A

    12. La wildcard route est définie par :

    Réponse : B

    13. *ngFor="let item of items" itère sur :

    Réponse : B

    14. NgModule regroupe :

    Réponse : A

    15. Pourquoi utiliser trackBy avec ngFor ?

    Réponse : B

    16. La liaison par interpolation utilise :

    Réponse : C

    17. AngularJS (v1) vs Angular (v2+) : Angular utilise :

    Réponse : B

    18. Un Pipe transforme :

    Réponse : B

    19. ng serve lance :

    Réponse : B

    20. Pour utiliser ngModel, quel module est nécessaire ?

    Réponse : B

    7. Spring Boot

    1. @SpringBootApplication combine :

    Réponse : B

    2. @RestController est équivalent à :

    Réponse : A

    3. @Autowired permet :

    Réponse : B

    4. Quelle annotation mappe une classe à une table BDD ?

    Réponse : B

    5. @Transactional assure :

    Réponse : B

    6. JpaRepository hérite de :

    Réponse : A

    7. @Id désigne :

    Réponse : B

    8. @ManyToOne est une relation :

    Réponse : B

    9. Quelle annotation mappe une requête HTTP GET ?

    Réponse : C

    10. @PathVariable extrait :

    Réponse : B

    11. FetchType.LAZY signifie :

    Réponse : B

    12. @Service est une spécialisation de :

    Réponse : B

    13. application.properties contient :

    Réponse : B

    14. @Valid déclenche :

    Réponse : B

    15. CascadeType.ALL propage :

    Réponse : B

    16. @JoinColumn est utilisé pour :

    Réponse : B

    17. @ControllerAdvice gère :

    Réponse : B

    18. Quelle est la méthode HTTP pour créer une ressource ?

    Réponse : B

    19. mvn spring-boot:run permet de :

    Réponse : B

    20. @OneToMany est l'inverse de :

    Réponse : B

    8. Laravel

    1. La commande pour lancer le serveur local est :

    Réponse : B

    2. Eloquent est :

    Réponse : B

    3. @csrf dans un formulaire sert à :

    Réponse : B

    4. Quelle méthode Eloquent récupère tous les enregistrements ?

    Réponse : B

    5. Blade est :

    Réponse : B

    6. php artisan make:model -m crée :

    Réponse : B

    7. $fillable dans un modèle définit :

    Réponse : B

    8. Route::resource crée :

    Réponse : B

    9. La validation 'required' signifie :

    Réponse : B

    10. @extends('layout') permet :

    Réponse : B

    11. findOrFail($id) retourne :

    Réponse : B

    12. Les migrations permettent :

    Réponse : B

    13. hasMany définit une relation :

    Réponse : B

    14. php artisan migrate:fresh :

    Réponse : B

    15. @yield('content') sert à :

    Réponse : B

    16. belongsToMany définit :

    Réponse : B

    17. Le dossier des contrôleurs est :

    Réponse : B

    18. with('relation') permet :

    Réponse : B

    19. Middleware dans Laravel agit comme :

    Réponse : B

    20. Envoyer une requête POST en Laravel crée souvent une donnée, quelle méthode est utilisée dans le contrôleur ?

    Réponse : B (store gère la logique POST)

    9. Réseaux

    1. Sur quelle couche OSI travaille un Switch classique ?

    Réponse : B

    2. Quel protocole est "non fiable" et rapide ?

    Réponse : B

    3. DNS traduit :

    Réponse : B

    4. DHCP attribue :

    Réponse : B

    5. Quelle est la couche du modèle OSI pour IP ?

    Réponse : C

    6. HTTP utilise par défaut le port :

    Réponse : C

    7. HTTPS sécurise via :

    Réponse : B

    8. Une adresse IPv4 fait :

    Réponse : C

    9. Le routeur connecte :

    Réponse : B

    10. ARP associe :

    Réponse : A

    11. TCP assure :

    Réponse : B

    12. Le port 443 correspond à :

    Réponse : B

    13. 192.168.1.1 est une adresse :

    Réponse : B

    14. VPN signifie :

    Réponse : A

    15. La commande ping utilise le protocole :

    Réponse : C

    16. SMTP est utilisé pour :

    Réponse : B

    17. Un Firewall sert à :

    Réponse : B

    18. FTP utilise le port :

    Réponse : A

    19. NAT permet :

    Réponse : A

    20. Le modèle TCP/IP simplifié a combien de couches ?

    Réponse : B

    📝 QCM Géant (400 Questions)

    📚 Catégories

    Aucun commentaire:

    Enregistrer un commentaire