06

Fonctions Avancées

Types, Closures, this & Patterns

Les fonctions sont des citoyens de première classe en JS. Elles peuvent être passées, retournées, et stockées comme n'importe quelle valeur.

🎯 Les 6 Types de Fonctions

1️⃣ Function Declaration
function greet(name) {
    return `Hello ${name}`;
}

// ✅ Hoisted (disponible avant)
// ✅ Nommée (stack traces)
Quand : Fonctions réutilisables, hoisting souhaité
2️⃣ Function Expression
const greet = function(name) {
    return `Hello ${name}`;
};

// ❌ Pas hoisted
// ✅ Assignable à variable
Quand : Callbacks, assignation conditionnelle
3️⃣ Arrow Function
const greet = (name) => `Hello ${name}`;

// ✅ Syntaxe concise
// ✅ Lexical this (hérite du parent)
// ❌ Pas de 'arguments'
Quand : Callbacks, méthodes array, pas besoin de `this`
4️⃣ IIFE (Immediately Invoked)
(function() {
    const secret = "privé";
    console.log(secret);
})();

// ✅ Scope isolé
// ✅ Exécution immédiate
Quand : Isolation de scope, module pattern (legacy)
5️⃣ Async Function
async function fetchData() {
    const res = await fetch(url);
    return res.json();
}

// ✅ Retourne toujours Promise
// ✅ Syntaxe await
Quand : Opérations asynchrones (API, I/O)
6️⃣ Generator Function
function* count() {
    yield 1;
    yield 2;
    yield 3;
}

// ✅ Pause/Resume avec yield
// ✅ Itérable
Quand : Itération lazy, state machines

🔒 Closures : Mémoire Persistante

Une closure permet à une fonction d'accéder aux variables de son scope parent, même après que ce parent ait terminé son exécution.

📦 Visualisation Mémoire

function createCounter() {
    let count = 0; // ← Variable capturée
    return function() {
        count++;
        return count;
    };
}

const counter = createCounter();
counter(); // 1
counter(); // 2 (count persiste !)
🌍 Global Scope
counter: function
📦 createCounter() Scope (Fermé mais accessible)
count: 2
🔒 Fonction retournée (Closure)
Accède à count via la closure chain

🎯 Le Mystère de `this`

La valeur de `this` dépend de comment la fonction est appelée, pas où elle est définie.

✅ Arrow Function (Lexical this)
const obj = {
    name: "Alice",
    greet: () => {
        // this = window/global
        console.log(this.name);
    }
};

// ⚠️ Arrow hérite du parent
✅ Regular Function
const obj = {
    name: "Alice",
    greet: function() {
        // this = obj
        console.log(this.name);
    }
};

obj.greet(); // "Alice"
// Les 4 façons de lier 'this'

// 1. Appel méthode : this = objet
obj.method();

// 2. Appel simple : this = undefined (strict) ou window
fn();

// 3. call/apply : this = premier argument
fn.call(customThis, arg1, arg2);
fn.apply(customThis, [arg1, arg2]);

// 4. bind : crée nouvelle fonction avec this fixé
const boundFn = fn.bind(customThis);
boundFn();

🎮 This Playground

Testez comment `this` change selon le contexte d'appel.

obj.method()
fn()
Arrow Function
fn.call(custom)
fn.bind(custom)
Cliquez sur un bouton pour tester

🧪 Pure Functions vs Side Effects

❌ Impure (Side Effects)
let total = 0;

function add(n) {
    total += n; // Modifie état externe
    return total;
}

// ❌ Non prédictible
// ❌ Difficile à tester
✅ Pure (Prédictible)
function add(a, b) {
    return a + b; // Pas d'effet de bord
}

// ✅ Même input = même output
// ✅ Testable
// ✅ Cacheable
Best Practice : Privilégiez les fonctions pures. Elles sont plus faciles à tester, débugger, et optimiser (memoization).
← Boucles Suivant: Arrays →