⚡ Fonctions Avancées

JavaScript

Maîtriser les concepts modernes

Formation DWWM - 2026

Au programme 📋

# 📚 Partie 0

Les Fonctions Basiques

Comprendre les fondamentaux

C'est quoi une fonction ? 🤔

Une fonction est un bloc de code réutilisable qui accomplit une tâche précise.

Analogie : La machine à café ☕

Une fonction est comme une machine à café :

Tu peux utiliser la machine autant de fois que tu veux, avec différents ingrédients !

// Fonction simple
function direBonjour() {
    console.log("Bonjour !");
}

// Utilisation (appel de la fonction)
direBonjour();  // "Bonjour !"
direBonjour();  // "Bonjour !"
direBonjour();  // "Bonjour !"

// On peut l'appeler autant de fois qu'on veut !

Pourquoi utiliser des fonctions ? 💡

Avantages des fonctions :

❌ Sans fonction (répétitif)

console.log("Marie" + " a " + 25 + " ans");
console.log("Jean" + " a " + 30 + " ans");
console.log("Paul" + " a " + 28 + " ans");

// 😰 Code répété !

✅ Avec fonction (réutilisable)

function presenter(nom, age) {
    console.log(nom + " a " + age + " ans");
}

presenter("Marie", 25);
presenter("Jean", 30);
presenter("Paul", 28);

// ✅ Simple et clair !

Déclaration de fonction 📝

La méthode la plus courante pour créer une fonction :

function nomFonction(parametres) {
// Corps de la fonction
// Code à exécuter
}
// Fonction sans paramètre
function direBonjour() {
    console.log("Bonjour tout le monde !");
}

direBonjour();  // "Bonjour tout le monde !"

// Fonction avec un paramètre
function saluer(nom) {
    console.log("Bonjour " + nom);
}

saluer("Marie");  // "Bonjour Marie"
saluer("Jean");   // "Bonjour Jean"

// Fonction avec plusieurs paramètres
function addition(a, b) {
    console.log(a + b);
}

addition(5, 3);   // 8
addition(10, 20); // 30

Paramètres vs Arguments 🔍

Quelle est la différence ?

Paramètres

Variables définies lors de la déclaration de la fonction

function saluer(nom, age) {
    //           ↑     ↑
    //      paramètres
}

Arguments

Valeurs réelles passées lors de l'appel de la fonction

saluer("Marie", 25);
//      ↑       ↑
//    arguments
function calculer(x, y, operation) {
    // x, y, operation = paramètres
    console.log(`${x} ${operation} ${y}`);
}

calculer(10, 5, "+");  // 10, 5, "+" = arguments
calculer(20, 4, "*");  // 20, 4, "*" = arguments

Résumé : Les paramètres sont les variables, les arguments sont les valeurs.

Le mot-clé return 🎯

return permet à une fonction de renvoyer un résultat à l'appelant.

Sans return (juste afficher)

function addition(a, b) {
    console.log(a + b);
}

addition(5, 3);  // Affiche 8
// Mais ne renvoie rien !

const resultat = addition(5, 3);
console.log(resultat);  // undefined

Avec return (renvoyer)

function addition(a, b) {
    return a + b;
}

addition(5, 3);  // Renvoie 8
// (rien affiché)

const resultat = addition(5, 3);
console.log(resultat);  // 8 ✅
// Utiliser le résultat dans d'autres calculs
function double(x) {
    return x * 2;
}

const nombre = double(5);        // 10
const encore = double(nombre);   // 20
const total = double(10) + double(20);  // 20 + 40 = 60

console.log(total);  // 60

⚠️ Important : Tout code après return n'est JAMAIS exécuté !

function test() {
    return "Fin";
    console.log("Jamais exécuté");  // ❌ Code mort
}

Return avancé 📤

On peut retourner n'importe quel type de valeur :

// Retourner un nombre
function calculer() {
    return 42;
}

// Retourner une chaîne
function getMessage() {
    return "Hello";
}

// Retourner un booléen
function estMajeur(age) {
    return age >= 18;
}

// Retourner un tableau
function creerTableau() {
    return [1, 2, 3, 4, 5];
}

// Retourner un objet
function creerPersonne() {
    return {
        nom: "Marie",
        age: 25
    };
}

// Retourner le résultat d'une autre fonction
function complexe() {
    return calculer() + 10;  // 42 + 10 = 52
}

Return conditionnel

function verifierAge(age) {
    if (age >= 18) {
        return "Majeur";
    } else {
        return "Mineur";
    }
}

console.log(verifierAge(20));  // "Majeur"
console.log(verifierAge(15));  // "Mineur"

Expression de fonction 🎨

On peut aussi assigner une fonction à une variable :

const nomVariable = function(parametres) {
// Corps de la fonction
};
// Expression de fonction
const saluer = function(nom) {
    return "Bonjour " + nom;
};

console.log(saluer("Marie"));  // "Bonjour Marie"

// Différence : pas de nom après "function"
const addition = function(a, b) {
    return a + b;
};

console.log(addition(5, 3));  // 8

Déclaration

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

// A un nom propre

Expression

const saluer = function(nom) {
    return "Hello " + nom;
};

// Fonction anonyme assignée

Hoisting (Remontée) 🎈

Le hoisting fait "remonter" les déclarations en haut du code.

Déclaration de fonction

// ✅ Fonctionne !
console.log(saluer("Marie"));

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

// La déclaration est "remontée"
// On peut l'appeler avant

Expression de fonction

// ❌ Erreur !
console.log(saluer("Marie"));

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

// L'expression n'est pas remontée
// On doit l'appeler après

Règle simple :

Les déclarations de fonction sont remontées, les expressions non.

// ✅ BON
function direBonjour() {
    return "Bonjour";
}
console.log(direBonjour());

// ✅ BON aussi
const direHello = function() {
    return "Hello";
};
console.log(direHello());

Portée des variables (Scope) 🔭

La portée détermine où une variable est accessible.

1. Variables globales

const nom = "Marie";  // Variable globale

function afficher() {
    console.log(nom);  // ✅ Accessible partout
}

afficher();  // "Marie"
console.log(nom);  // "Marie"

2. Variables locales

function afficher() {
    const nom = "Jean";  // Variable locale
    console.log(nom);    // ✅ Accessible dans la fonction
}

afficher();      // "Jean"
console.log(nom);  // ❌ Erreur : nom n'existe pas ici !

3. Paramètres (locaux à la fonction)

function saluer(nom) {  // nom est un paramètre local
    console.log(nom);
}

saluer("Paul");    // "Paul"
console.log(nom);  // ❌ Erreur : nom n'existe pas ici !

Scope : Cas pratiques 🎯

Variable globale vs locale

const message = "Global";  // Variable globale

function test1() {
    const message = "Local";  // Variable locale (même nom !)
    console.log(message);     // "Local" (priorité au local)
}

test1();              // "Local"
console.log(message); // "Global"

Accès du local au global

const prenom = "Marie";  // Globale

function afficherNom() {
    const nom = "Dupont";  // Locale
    
    // ✅ Peut accéder aux deux
    console.log(prenom + " " + nom);
}

afficherNom();  // "Marie Dupont"

⚠️ Attention aux modifications !

let compteur = 0;  // Globale

function incrementer() {
    compteur++;  // ⚠️ Modifie la variable globale !
}

incrementer();
console.log(compteur);  // 1

incrementer();
console.log(compteur);  // 2

Fonctions anonymes 👻

Une fonction anonyme est une fonction sans nom.

// Fonction anonyme assignée à une variable
const saluer = function(nom) {
    return "Bonjour " + nom;
};

// Fonction anonyme passée en callback
setTimeout(function() {
    console.log("Exécuté après 2 secondes");
}, 2000);

// Fonction anonyme dans un tableau
const operations = [
    function(x) { return x * 2; },
    function(x) { return x + 10; },
    function(x) { return x * x; }
];

console.log(operations[0](5));  // 10
console.log(operations[1](5));  // 15
console.log(operations[2](5));  // 25

Utilisation courante : Callbacks, événements, et situations où on n'a pas besoin de réutiliser la fonction.

Bonnes pratiques 💡

  1. Nommer clairement les fonctions
    calculerTotal() plutôt que calc()
  2. Une fonction = une tâche
    Éviter les fonctions qui font trop de choses
  3. Limiter le nombre de paramètres
    Maximum 3-4 paramètres, sinon utiliser un objet
  4. Toujours retourner quelque chose
    Éviter les fonctions qui modifient des variables globales
  5. Documenter les fonctions complexes
    Ajouter des commentaires explicatifs

❌ Mauvais exemple

function f(a, b, c, d, e) {
    // Trop de paramètres
    // Nom peu clair
    // Que fait cette fonction ?
}

✅ Bon exemple

function calculerMoyenne(notes) {
    // Nom clair
    // 1 seul paramètre (tableau)
    const somme = notes.reduce((a,b) => a+b);
    return somme / notes.length;
}

Récapitulatif Fonctions Basiques 📚

Vous savez maintenant :

Maintenant, passons aux fonctions avancées ! 🚀

# ➡️ Partie 1

Arrow Functions

Les fonctions fléchées modernes

C'est quoi une arrow function ? 🏹

Une arrow function (fonction fléchée) est une syntaxe plus courte pour écrire des fonctions, introduite en ES6 (2015).

Fonction classique

const double = function(x) {
    return x * 2;
};

Arrow function

const double = (x) => {
    return x * 2;
};
Le mot function disparaît
Une flèche => apparaît

C'est la même chose, juste écrit différemment !

Syntaxe de base 🔍

const nomFonction = (parametres) => {
↓ ↓
params flèche

// Corps de la fonction
return valeur;
};
// Exemple avec plusieurs paramètres
const addition = (a, b) => {
    return a + b;
};

// Exemple avec un seul paramètre
const carre = (x) => {
    return x * x;
};

// Exemple sans paramètre
const direBonjour = () => {
    return "Bonjour !";
};

console.log(addition(5, 3));   // 8
console.log(carre(4));          // 16
console.log(direBonjour());     // "Bonjour !"

Return implicite : encore plus court ! ⚡

Si la fonction ne contient qu'une seule ligne qui retourne une valeur, on peut omettre les accolades et le return !

Avec return explicite

const double = (x) => {
    return x * 2;
};

const addition = (a, b) => {
    return a + b;
};

Return implicite (plus court)

const double = (x) => x * 2;


const addition = (a, b) => a + b;

Les deux versions fonctionnent exactement pareil ! La deuxième est juste plus concise.

// Autres exemples
const estMajeur = (age) => age >= 18;
const carre = (n) => n * n;
const saluer = (nom) => `Bonjour ${nom}`;

console.log(estMajeur(20));       // true
console.log(carre(5));             // 25
console.log(saluer("Marie"));      // "Bonjour Marie"

Parenthèses des paramètres 🤔

Les parenthèses autour des paramètres sont parfois optionnelles :

Nombre de paramètres Parenthèses Exemple
Aucun paramètre OBLIGATOIRES () => "Hello"
Un seul paramètre OPTIONNELLES x => x * 2 ou (x) => x * 2
Plusieurs paramètres OBLIGATOIRES (a, b) => a + b
// Sans paramètre : () obligatoires
const hello = () => "Hello";

// 1 paramètre : parenthèses au choix
const double1 = x => x * 2;        // ✅ OK
const double2 = (x) => x * 2;      // ✅ OK aussi

// 2+ paramètres : () obligatoires
const add = (a, b) => a + b;       // ✅ OK
// const add = a, b => a + b;      // ❌ ERREUR

Conseil : Pour la cohérence, mets toujours les parenthèses même avec 1 paramètre !

Cas particulier : Retourner un objet 🎯

Pour retourner un objet avec return implicite, il faut entourer l'objet de parenthèses !

❌ ERREUR

// JavaScript pense que {} = bloc de code
const creerUser = nom => {nom: nom};

// Erreur de syntaxe !

✅ CORRECT

// Parenthèses autour de l'objet
const creerUser = nom => ({nom: nom});

console.log(creerUser("Marie"));
// {nom: "Marie"} ✅
// Autres exemples
const creerPoint = (x, y) => ({x: x, y: y});
const creerPersonne = (n, a) => ({nom: n, age: a});

console.log(creerPoint(10, 20));
// {x: 10, y: 20}

console.log(creerPersonne("Jean", 25));
// {nom: "Jean", age: 25}

Différence importante : le "this" ⚠️

Les arrow functions ont un comportement différent avec le mot-clé this.

Règle clé :

Une arrow function n'a pas son propre this. Elle utilise le this du contexte parent (portée lexicale).

const utilisateur = {
    nom: "Marie",
    
    // Fonction classique
    saluerClassique: function() {
        console.log("Bonjour, je suis " + this.nom);
    },
    
    // Arrow function
    saluerArrow: () => {
        console.log("Bonjour, je suis " + this.nom);
    }
};

utilisateur.saluerClassique();  // "Bonjour, je suis Marie" ✅
utilisateur.saluerArrow();      // "Bonjour, je suis undefined" ❌

⚠️ Important : N'utilise PAS les arrow functions comme méthodes d'objet si tu as besoin de this !

Quand utiliser les arrow functions ? 💡

✅ Utilise arrow functions pour :

❌ N'utilise PAS arrow functions pour :

// ✅ Bon usage
const nombres = [1, 2, 3, 4];
const doubles = nombres.map(n => n * 2);

setTimeout(() => console.log("Hello"), 1000);

// ❌ Mauvais usage
const user = {
    nom: "Marie",
    saluer: () => console.log(this.nom)  // this incorrect
};
# 🎯 Partie 2

Paramètres par Défaut

Valeurs par défaut automatiques

Le problème avant ES6 😰

Avant ES6, si un paramètre n'était pas fourni, il valait undefined :

// Ancienne méthode
function saluer(nom) {
    console.log("Bonjour " + nom);
}

saluer("Marie");    // "Bonjour Marie" ✅
saluer();           // "Bonjour undefined" ❌

Il fallait vérifier manuellement et assigner une valeur :

function saluer(nom) {
    // Vérification manuelle
    if (nom === undefined) {
        nom = "Invité";
    }
    // Ou avec court-circuit
    nom = nom || "Invité";
    
    console.log("Bonjour " + nom);
}

saluer();  // "Bonjour Invité"

// 😰 Lourd et répétitif !

La solution ES6 : Paramètres par défaut 🎯

Avec ES6, on peut définir une valeur par défaut directement dans la signature !

function nomFonction(param = valeurParDefaut) {
// Si param n'est pas fourni, il vaut valeurParDefaut
}
// Syntaxe moderne
function saluer(nom = "Invité") {
    console.log(`Bonjour ${nom}`);
}

saluer("Marie");    // "Bonjour Marie"
saluer();           // "Bonjour Invité" ✅

// Avec arrow function
const direHello = (nom = "Anonyme") => `Hello ${nom}`;

console.log(direHello("Jean"));   // "Hello Jean"
console.log(direHello());         // "Hello Anonyme"

Avantage : Plus besoin de vérifier ! La valeur par défaut est utilisée automatiquement.

Plusieurs paramètres par défaut 📝

On peut mettre des valeurs par défaut sur plusieurs paramètres :

function creerUtilisateur(nom = "Anonyme", age = 18, ville = "Paris") {
    return {nom, age, ville};
}

// Tous les paramètres fournis
console.log(creerUtilisateur("Marie", 25, "Lyon"));
// {nom: "Marie", age: 25, ville: "Lyon"}

// Certains paramètres omis
console.log(creerUtilisateur("Jean", 30));
// {nom: "Jean", age: 30, ville: "Paris"}

// Tous omis
console.log(creerUtilisateur());
// {nom: "Anonyme", age: 18, ville: "Paris"}

Pour omettre un paramètre du milieu :

// Passer undefined pour utiliser la valeur par défaut
console.log(creerUtilisateur(undefined, 22, "Nice"));
// {nom: "Anonyme", age: 22, ville: "Nice"}

Valeurs par défaut calculées 🧮

La valeur par défaut peut être une expression ou un appel de fonction !

// Expression
function calculer(x, multiplicateur = x * 2) {
    return x * multiplicateur;
}

console.log(calculer(5));      // 5 * 10 = 50
console.log(calculer(5, 3));   // 5 * 3 = 15

// Appel de fonction
function obtenirDate() {
    return new Date().toLocaleDateString();
}

function logger(message, date = obtenirDate()) {
    console.log(`[${date}] ${message}`);
}

logger("Démarrage");              // [23/01/2025] Démarrage
logger("Erreur", "20/01/2025");   // [20/01/2025] Erreur

// Référence à un autre paramètre
function presenter(prenom, nom, nomComplet = `${prenom} ${nom}`) {
    console.log(nomComplet);
}

presenter("Marie", "Dupont");              // "Marie Dupont"
presenter("Jean", "Martin", "J. Martin");  // "J. Martin"
# 📦 Partie 3

Rest Parameters (...)

Nombre variable de paramètres

Le problème : Nombre variable d'arguments 🤔

Parfois, on ne sait pas à l'avance combien d'arguments on va recevoir :

Exemples :

// Comment additionner un nombre variable de nombres ?
// ❌ Pas pratique :
function additionner(a, b, c, d, e, f...) {
    // Impossible de lister tous les paramètres !
}

// Solution old-school : objet arguments
function additionnerOld() {
    let total = 0;
    for (let i = 0; i < arguments.length; i++) {
        total += arguments[i];
    }
    return total;
}

console.log(additionnerOld(1, 2, 3, 4, 5));  // 15
// Fonctionne mais déprécié !

La solution : Rest operator (...) 📦

Le rest operator (...) rassemble tous les arguments dans un tableau :

function nomFonction(...nomTableau) {
// nomTableau est un vrai tableau Array
// contenant TOUS les arguments
}
// Syntaxe moderne avec rest
function additionner(...nombres) {
    console.log(nombres);  // C'est un tableau !
    
    let total = 0;
    for (let n of nombres) {
        total += n;
    }
    return total;
}

console.log(additionner(1, 2, 3));           // 6
console.log(additionner(5, 10, 15, 20));     // 50
console.log(additionner(1, 2, 3, 4, 5, 6));  // 21

// Avec méthode reduce (plus moderne)
const additionner2 = (...nombres) => {
    return nombres.reduce((total, n) => total + n, 0);
};

console.log(additionner2(10, 20, 30));  // 60

Rest combiné avec d'autres paramètres 🎯

On peut combiner des paramètres normaux avec rest (mais rest doit être le dernier) :

// ✅ Correct : rest en dernier
function presenter(prenom, nom, ...hobbies) {
    console.log(`Je suis ${prenom} ${nom}`);
    console.log(`Mes hobbies : ${hobbies.join(", ")}`);
}

presenter("Marie", "Dupont", "lecture", "sport", "musique");
// Je suis Marie Dupont
// Mes hobbies : lecture, sport, musique

// Autre exemple : logger avec niveau
function logger(niveau, ...messages) {
    const timestamp = new Date().toLocaleTimeString();
    console.log(`[${timestamp}] [${niveau}]`, ...messages);
}

logger("INFO", "Connexion", "réussie");
// [14:30:25] [INFO] Connexion réussie

logger("ERROR", "Erreur", "de", "connexion");
// [14:30:26] [ERROR] Erreur de connexion

⚠️ Règles importantes :

// ❌ ERREUR : rest pas en dernier
// function mauvais(...args, dernierParam) { }

// ❌ ERREUR : plusieurs rest
// function mauvais(...args1, ...args2) { }

Rest (...) vs arguments 🆚

Caractéristique arguments (old) ...rest (moderne)
Type Objet array-like Vrai tableau Array
Méthodes ❌ Pas map, filter... ✅ Toutes les méthodes
Arrow functions ❌ Pas disponible ✅ Fonctionne
Moderne ❌ Déprécié ✅ Standard ES6+

Conseil : Utilise TOUJOURS ...rest, jamais arguments !

// ✅ Moderne
const max = (...nombres) => Math.max(...nombres);
console.log(max(5, 12, 3, 99, 42));  // 99

// ❌ Déprécié
function maxOld() {
    return Math.max.apply(null, arguments);
}
# 🔄 Partie 4

Fonctions Callback

Passer des fonctions en paramètres

C'est quoi une fonction callback ? 📞

Une fonction callback (fonction de rappel) est une fonction passée en paramètre à une autre fonction.

Analogie : Le plombier 🔧

Tu appelles un plombier pour réparer ta douche. Tu lui dis :

"Quand tu as fini, appelle-moi (callback) pour que je vérifie"

Le numéro de téléphone que tu donnes = callback ! Tu ne restes pas à attendre, tu fais autre chose.

// Fonction qui prend une callback en paramètre
function direBonjour(nom, callback) {
    console.log(`Bonjour ${nom}`);
    
    // Appeler la fonction callback
    callback();
}

// Utilisation
direBonjour("Marie", function() {
    console.log("Ravi de te rencontrer !");
});

// Résultat :
// Bonjour Marie
// Ravi de te rencontrer !

Pourquoi utiliser des callbacks ? 💡

Avantages des callbacks :

// Exemple : fonction générique
function traiterNombres(a, b, operation) {
    return operation(a, b);  // On appelle la callback
}

// Utilisation avec différentes callbacks
const addition = (x, y) => x + y;
const multiplication = (x, y) => x * y;
const max = (x, y) => x > y ? x : y;

console.log(traiterNombres(5, 3, addition));        // 8
console.log(traiterNombres(5, 3, multiplication));  // 15
console.log(traiterNombres(5, 3, max));             // 5

// Même fonction, comportements différents !

Callback inline (anonyme) 🎯

On peut définir la callback directement lors de l'appel :

Fonction séparée

function callback() {
    console.log("Fait !");
}

traiter(callback);

Callback inline

traiter(function() {
    console.log("Fait !");
});

Encore mieux : avec arrow function

traiter(() => {
    console.log("Fait !");
});

// Version ultra-courte (1 ligne)
traiter(() => console.log("Fait !"));

Les arrow functions sont parfaites pour les callbacks courts !

Callbacks avec les méthodes de tableaux 🎯

Les callbacks sont massivement utilisés avec les méthodes comme map, filter, forEach...

forEach : exécuter pour chaque élément

const fruits = ["pomme", "banane", "orange"];

fruits.forEach((fruit, index) => {
    console.log(`${index}: ${fruit}`);
});
// 0: pomme
// 1: banane
// 2: orange

map : transformer chaque élément

const nombres = [1, 2, 3, 4];

const doubles = nombres.map(n => n * 2);
console.log(doubles);  // [2, 4, 6, 8]

filter : filtrer selon une condition

const ages = [12, 18, 25, 15, 30];

const majeurs = ages.filter(age => age >= 18);
console.log(majeurs);  // [18, 25, 30]

Callbacks asynchrones ⏱️

Les callbacks sont essentiels pour les opérations asynchrones (qui prennent du temps).

setTimeout : exécuter après un délai

console.log("Début");

setTimeout(() => {
    console.log("3 secondes écoulées");
}, 3000);

console.log("Fin");

// Affichage :
// "Début"
// "Fin"
// ... (3 secondes) ...
// "3 secondes écoulées"

Explication :

Le callback () => { ... } est exécuté plus tard, après 3000ms. Le code continue pendant ce temps !

setInterval : répéter une action

let compteur = 0;

const interval = setInterval(() => {
    compteur++;
    console.log(`Compteur: ${compteur}`);
    
    if (compteur === 5) {
        clearInterval(interval);  // Arrêter
    }
}, 1000);

// Affiche toutes les secondes : 1, 2, 3, 4, 5
# 🔒 Partie 5

Closures

Fonctions qui se souviennent

C'est quoi une closure ? 🔒

Une closure (fermeture) est une fonction qui "se souvient" des variables de son environnement, même après que la fonction parent ait terminé.

Analogie : Le sac à dos 🎒

Imagine une fonction comme une personne avec un sac à dos. Quand elle est créée dans une autre fonction, elle met dans son sac toutes les variables accessibles.

Même si elle part loin (la fonction parent termine), elle garde son sac ! Elle peut toujours accéder à ces variables.

C'est un concept fondamental et très puissant de JavaScript !

Exemple simple de closure 🎯

function creerCompteur() {
    let compteur = 0;  // Variable locale
    
    // Cette fonction "se souvient" de compteur
    return function() {
        compteur++;
        return compteur;
    };
}

const monCompteur = creerCompteur();

console.log(monCompteur());  // 1
console.log(monCompteur());  // 2
console.log(monCompteur());  // 3
console.log(monCompteur());  // 4
1. creerCompteur() crée compteur = 0
2. Retourne une fonction qui utilise compteur
3. Cette fonction "capture" compteur (closure)
4. Même si creerCompteur() a fini, compteur existe toujours !
5. Chaque appel modifie LA MÊME variable compteur

Pourquoi ça marche ? ⚙️

Normalement, quand une fonction termine, ses variables locales disparaissent. Mais avec une closure, elles restent en mémoire !

  1. Création
    creerCompteur() est appelée, créant compteur = 0
  2. Retour d'une fonction
    Une fonction interne est retournée et stockée dans monCompteur
  3. Capture (closure)
    Cette fonction "capture" l'environnement, incluant compteur
  4. Persistance
    Même si creerCompteur() a fini, compteur reste accessible
  5. État privé
    compteur ne peut être modifié que par la fonction retournée

Portée lexicale (Lexical Scope) 📖

La portée lexicale signifie que les fonctions ont accès aux variables là où elles sont définies, pas où elles sont appelées.

const nom = "Global";

function externe() {
    const nom = "Externe";
    
    function interne() {
        console.log(nom);  // Quelle valeur ?
    }
    
    return interne;
}

const maFonction = externe();
maFonction();  // "Externe" ← Portée lexicale !

// interne() "voit" le nom de là où elle est DÉFINIE
// Pas le nom global

Règle : Une fonction a accès aux variables de son scope parent, de son grand-parent, etc., jusqu'au scope global.

Closures indépendantes 🎯

Chaque appel à la fonction externe crée une nouvelle closure avec son propre environnement.

function creerCompteur() {
    let compteur = 0;
    return function() {
        compteur++;
        return compteur;
    };
}

const compteur1 = creerCompteur();
const compteur2 = creerCompteur();

console.log(compteur1());  // 1
console.log(compteur1());  // 2
console.log(compteur1());  // 3

console.log(compteur2());  // 1 ← Compteur indépendant !
console.log(compteur2());  // 2

console.log(compteur1());  // 4 ← Continue son propre compteur

Important : Chaque closure a sa propre "copie" des variables. Elles sont complètement indépendantes !

À quoi servent les closures ? 💡

Cas d'usage des closures :

Exemple : Données privées

function creerBanque() {
    let solde = 0;  // Variable privée !
    
    return {
        deposer(montant) {
            solde += montant;
            console.log(`Dépôt: ${montant}€. Solde: ${solde}€`);
        },
        retirer(montant) {
            if (montant <= solde) {
                solde -= montant;
                console.log(`Retrait: ${montant}€. Solde: ${solde}€`);
            } else {
                console.log("Solde insuffisant");
            }
        },
        consulter() {
            console.log(`Solde actuel: ${solde}€`);
        }
    };
}

const monCompte = creerBanque();
monCompte.deposer(100);   // Dépôt: 100€. Solde: 100€
monCompte.retirer(30);    // Retrait: 30€. Solde: 70€

// ❌ Impossible d'accéder directement à solde !
console.log(monCompte.solde);  // undefined

Factory Functions avec Closures 🏭

Une factory function est une fonction qui crée et retourne des objets/fonctions personnalisés.

function creerMultiplicateur(facteur) {
    // facteur est capturé dans une closure
    return function(nombre) {
        return nombre * facteur;
    };
}

const doubler = creerMultiplicateur(2);
const tripler = creerMultiplicateur(3);
const quadrupler = creerMultiplicateur(4);

console.log(doubler(5));      // 10 (5 × 2)
console.log(tripler(5));      // 15 (5 × 3)
console.log(quadrupler(5));   // 20 (5 × 4)

// Chaque fonction "se souvient" de son facteur !

Autre exemple : créer des salutations

function creerSalutation(salut) {
    return function(nom) {
        return `${salut} ${nom} !`;
    };
}

const direBonjour = creerSalutation("Bonjour");
const direHello = creerSalutation("Hello");

console.log(direBonjour("Marie"));  // "Bonjour Marie !"
console.log(direHello("John"));     // "Hello John !"
# ⚡ Partie 6

IIFE

Immediately Invoked Function Expression

C'est quoi une IIFE ? ⚡

IIFE (prononcé "ifi") = Fonction qui s'exécute immédiatement dès qu'elle est définie.

Analogie : Le feu d'artifice 🎆

C'est comme un feu d'artifice : dès que tu l'allumes, il explose. Pas besoin de l'appeler après, il se déclenche tout seul !

(function() {
// Code ici
})();
↓ ↓
Fonction Exécution immédiate !

Les parenthèses () à la fin exécutent la fonction tout de suite.

Syntaxe des IIFE 🔍

Il y a deux syntaxes équivalentes :

Syntaxe 1 (classique)

(function() {
    console.log("Je m'exécute !");
})();

Syntaxe 2 (alternative)

(function() {
    console.log("Je m'exécute !");
}());

Avec arrow function (moderne)

(() => {
    console.log("IIFE avec arrow function !");
})();

// Version ultra-courte
(() => console.log("Court !"))();

Pourquoi utiliser des IIFE ? 🤔

Raisons d'utiliser des IIFE :

Exemple : Éviter pollution globale

// ❌ Sans IIFE : variables globales
var compteur = 0;
var nom = "App";
// Risque de conflit avec d'autres scripts !

// ✅ Avec IIFE : variables isolées
(function() {
    var compteur = 0;
    var nom = "App";
    // Ces variables n'existent que dans l'IIFE
})();

console.log(typeof compteur);  // "undefined"
console.log(typeof nom);       // "undefined"

IIFE avec paramètres 📨

Une IIFE peut recevoir des paramètres !

(function(nom, age) {
    console.log(`Bonjour ${nom}, tu as ${age} ans`);
})("Marie", 25);

// Résultat : "Bonjour Marie, tu as 25 ans"

Exemple : Initialisation avec config

(function(config) {
    console.log("=== INITIALISATION ===");
    console.log(`Environnement: ${config.env}`);
    console.log(`Debug: ${config.debug}`);
    console.log(`Version: ${config.version}`);
})({
    env: "production",
    debug: false,
    version: "1.0.0"
});

// S'exécute immédiatement au chargement !

IIFE qui retournent une valeur 🎯

Une IIFE peut retourner une valeur directement assignée :

const resultat = (function() {
    const a = 10;
    const b = 20;
    return a + b;
})();

console.log(resultat);  // 30

Exemple : Créer un module

const monModule = (function() {
    // Variables privées
    let compteur = 0;
    
    // Méthodes publiques (retournées)
    return {
        incrementer() {
            compteur++;
        },
        obtenir() {
            return compteur;
        },
        reset() {
            compteur = 0;
        }
    };
})();

// Utilisation
monModule.incrementer();
monModule.incrementer();
console.log(monModule.obtenir());  // 2

// compteur est inaccessible de l'extérieur !

Les IIFE aujourd'hui 📅

Avec ES6 modules et let/const, les IIFE sont moins utilisées qu'avant.

Avant (ES5)

// IIFE pour isolation
(function() {
    var secret = "privé";
    // ...
})();

Maintenant (ES6+)

// Block scope
{
    let secret = "privé";
    // ...
}
// secret n'existe plus

Quand utiliser des IIFE aujourd'hui ?

# ✨ Partie 7

Exemples Complets

Mise en pratique

Exemple 1 : Gestionnaire de tâches 📝

const gestionTaches = (() => {
    let taches = [];
    let id = 0;
    
    return {
        ajouter: (titre) => {
            taches.push({id: ++id, titre, fait: false});
            console.log(`✅ Ajouté: ${titre}`);
        },
        
        terminer: (taskId) => {
            const tache = taches.find(t => t.id === taskId);
            if (tache) {
                tache.fait = true;
                console.log(`✓ "${tache.titre}" terminée`);
            }
        },
        
        lister: () => {
            console.log("=== MES TÂCHES ===");
            taches.forEach(t => {
                const statut = t.fait ? "✓" : "○";
                console.log(`${statut} ${t.id}. ${t.titre}`);
            });
        }
    };
})();

gestionTaches.ajouter("Faire les courses");
gestionTaches.ajouter("Apprendre JavaScript");
gestionTaches.terminer(2);
gestionTaches.lister();

Exemple 2 : Debounce function ⏱️

// Fonction qui retarde l'exécution (utile pour recherche)
function debounce(callback, delai = 300) {
    let timeoutId;
    
    return function(...args) {
        clearTimeout(timeoutId);
        
        timeoutId = setTimeout(() => {
            callback(...args);
        }, delai);
    };
}

// Utilisation
const rechercher = (terme) => {
    console.log(`🔍 Recherche: ${terme}`);
};

const rechercherDebounced = debounce(rechercher, 500);

// Simule des frappes
rechercherDebounced("j");
rechercherDebounced("ja");
rechercherDebounced("jav");
rechercherDebounced("javas");
rechercherDebounced("javascript");

// N'exécute qu'une fois, 500ms après la dernière frappe
// Résultat : 🔍 Recherche: javascript
# 🎓 Récapitulatif

Ce qu'on a appris

Résumé des concepts 📚

Concept Syntaxe Utilité
Arrow Function (x) => x * 2 Syntaxe courte, this lexical
Paramètres défaut function(a = 10) Valeurs par défaut automatiques
Rest paramètres function(...args) Nombre variable d'arguments
Callback func(callback) Fonction passée en paramètre
Closure Fonction interne Préserver l'environnement
IIFE (() => {})() Exécution immédiate

Bonnes pratiques 💡

  1. Arrow functions pour callbacks courts
    array.map(x => x * 2) plutôt que function
  2. Paramètres par défaut en dernier
    function(requis, optionnel = valeur)
  3. Rest pour arguments variables
    Préfère ...args à arguments
  4. Closures pour encapsulation
    Créer des variables privées
  5. IIFE moins utilisées aujourd'hui
    Préfère les modules ES6

🎓 Fin du cours

Fonctions Avancées JavaScript

Vous maîtrisez maintenant :

➡️ Arrow functions pour du code concis

🎯 Paramètres par défaut et rest

🔄 Callbacks pour l'asynchrone

🔒 Closures pour l'encapsulation

⚡ IIFE pour l'isolation

Continuez à pratiquer ! 💪

Formation DWWM - 2026