Nouveau : Datasets open source gratuits disponibles !Decouvrir →
🟨
Avance 20 min JavaScript

Les closures en JavaScript

Pourquoi closures en JavaScript ?

Les closures en JavaScript sont essentielles car elles permettent aux fonctions d'accéder et de manipuler les variables déclarées dans leur portée parente, même après que cette fonction parente n'ait terminé d'exécuter. Cette fonctionnalité est cruciale pour créer des fonctions avec une mémoire persistante et pour concevoir des patterns comme le singleton, le module et la curryfication.

Un cas concret de closure en JavaScript est la création d'une fonction qui maintient un état interne entre les appels successifs. Par exemple, si vous créez une fonction qui incrémente un compteur à chaque appel, cette fonction devrait avoir accès au compteur même après que l'initialisation ait été effectuée.

Prerequis

  • Comprendre les concepts de portées (scope) et de variables en JavaScript.
  • Savoir comment déclarer des fonctions en JavaScript.
  • Avoir une connaissance de base des opérations sur les types de données en JavaScript.

Concepts fondamentaux

1. Portée et Variables

Les closures sont basées sur la portée des variables. En JavaScript, il existe trois niveaux de portée : le global, le bloc (à partir d'ES6 avec let et const) et la fonction.

var x = 10;

function outerFunction() {
    # Exemple de portée fonctionnelle
    var y = 20;
    
    function innerFunction() {
        # Exemple de portée bloc avec let/const (ES6)
        const z = 30;
        console.log(x, y, z); // Accessible car dans la même portée
    }
    
    return innerFunction; // Retourne la fonction interne
}
## 
var myClosure = outerFunction();
myClosure(); // Affiche 10 20 30

2. Fonction interne accédant aux variables externes

L'une des caractéristiques majeures d'une closure est qu'elle peut accéder à la portée de sa fonction parente.

function createCounter() {
    let count = 0; // Variable locale à createCounter
    
    function increment() { // Fonction interne qui accède à count
        count++;
        return count;
    }
    
    return increment; // Retourne la fonction interne
}
## 
const counter = createCounter();
console.log(counter()); // Affiche 1
console.log(counter()); // Affiche 2

3. Closures et Memory Management

Les closures sont utilisées pour maintenir des variables en mémoire même après que la fonction parente n'ait terminé d'exécuter.

function createTimer() {
    let startTime = Date.now();
    
    function getTimePassed() {
        return (Date.now() - startTime) + 'ms';
    }
    
    return getTimePassed;
}

const timer = createTimer();
console.log(timer()); // Affiche le temps écoulé depuis l'appel de createTimer

Mise en pratique : projet fil rouge

Étape 1: Définition des structures de données et fonctions principales

mkdir counter-app
cd counter-app
npm init -y

Créez un fichier index.js avec le contenu suivant :

// index.js
const fs = require('fs');
const readline = require('readline');

function createCounter() {
    let count = 0;

    function increment() {
        count++;
        return count;
    }

    function decrement() {
        count--;
        return count;
    }

    function getCount() {
        return count;
    }

    return { increment, decrement, getCount };
}

const counter = createCounter();
console.log(`Current count: ${counter.getCount()}`);

counter.increment();
console.log(`After increment: ${counter.getCount()}`);

counter.decrement();
console.log(`After decrement: ${counter.getCount()}`);

Étape 2: Ajout de l'interface utilisateur avec readline

Créez un fichier cli.js avec le contenu suivant :

// cli.js
const counter = require('./index');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

function showMenu() {
    console.log('1. Increment');
    console.log('2. Decrement');
    console.log('3. Get Count');
    console.log('4. Exit');
}

async function main() {
    while (true) {
        showMenu();
        const choice = await new Promise(resolve => rl.question('Choose an option: ', resolve));

        switch (choice) {
            case '1':
                counter.increment();
                break;
            case '2':
                counter.decrement();
                break;
            case '3':
                console.log(`Current count: ${counter.getCount()}`);
                break;
            case '4':
                rl.close();
                return;
            default:
                console.log('Invalid option. Please try again.');
        }
    }
}

main().catch(console.error);

Étape 3: Ajout de la gestion des erreurs

Dans index.js, ajoutez une gestion des erreurs pour les opérations sur le compteur :

// index.js
const fs = require('fs');
const readline = require('readline');

function createCounter() {
    let count = 0;

    function increment() {
        if (count < 10) {
            count++;
        } else {
            throw new Error('Count cannot exceed 10');
        }
        return count;
    }

    function decrement() {
        if (count > 0) {
            count--;
        } else {
            throw new Error('Count cannot be less than 0');
        }
        return count;
    }

    function getCount() {
        return count;
    }

    return { increment, decrement, getCount };
}

const counter = createCounter();
try {
    console.log(`Current count: ${counter.getCount()}`);
    counter.increment();
    console.log(`After increment: ${counter.getCount()}`);
    counter.decrement();
    console.log(`After decrement: ${counter.getCount()}`);
} catch (error) {
    console.error(error.message);
}

rl.question('Press enter to exit...', () => process.exit());

Étape 4: Exécution du projet

node cli.js

Ce mini-projet complet permet de créer une application en ligne de commande qui utilise une closure pour maintenir un état interne et gérer des erreurs. Les utilisateurs peuvent incrémenter, décrémenter et obtenir la valeur actuelle du compteur.

Erreurs frequentes et debugging

1. Erreur : Uncaught ReferenceError: count is not defined

function createCounter() {
    let count = 0;

    function increment() {
        count++; // Erreur ici : count n'est pas défini dans la portée de increment
        return count;
    }

    return increment;
}
javascript## 
function createCounter() {
    let count = 0;

    function increment() {
        count++;
        return count;
    }

    return { increment }; // Retourne l'objet avec la méthode increment
}

2. Erreur : Uncaught TypeError: Cannot read property 'getCount' of undefined

const counter = createCounter();
console.log(counter.getCount()); // Erreur ici : getCount n'est pas une fonction de counter
javascript## 
const counter = createCounter();
console.log(counter.getCount()); // Affiche la valeur actuelle du compteur

3. Erreur : Uncaught Error: Count cannot exceed 10

counter.increment();
counter.increment();
counter.increment();
counter.increment();
counter.increment(); // Erreur ici : count dépasse 10
javascript## 
try {
    counter.increment();
    counter.increment();
    counter.increment();
    counter.increment();
    counter.increment();
} catch (error) {
    console.error(error.message); // Affiche "Count cannot exceed 10"
}

Pour aller plus loin

1. Understanding the Event Loop in JavaScript

  • En savoir plus sur le cycle d'événements en JavaScript pour comprendre comment les closures fonctionnent avec des opérations asynchrones.
  • MDN Web Docs: The Event Loop

2. Functional Programming in JavaScript

3. Advanced JavaScript Concepts

  • Explorer d'autres concepts avancés de JavaScript tels que les prototypage, les modules ES6 et les classes pour approfondir votre compréhension des closures.
  • MDN Web Docs: JavaScript advanced topics

Défi pratique

Créez une application de gestion de notes en utilisant des closures. L'application devrait permettre aux utilisateurs d'ajouter, de supprimer et de récupérer les notes. Implémentez la gestion des erreurs pour les opérations sur les notes.

Besoin d'aide sur JavaScript ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Qu'est-ce qu'une closure en JavaScript ?
Une closure est une fonction qui a accès au contexte lexical (environnement) dans lequel elle a été créée, même si cette fonction est exécutée en dehors de ce contexte.
Comment créer une closure ?
Pour créer une closure, vous devez définir une fonction à l'intérieur d'une autre fonction. La fonction interne accède à des variables déclarées dans la fonction externe, même après que la fonction externe ait terminé son exécution.
Quels sont les avantages des closures en JavaScript ?
Les closures offrent plusieurs avantages : elles permettent de protéger les variables et les fonctions (encapsulation), facilitent la création d'objets avec des méthodes privées, et permettent une meilleure organisation du code.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.