Les Fonctions

Présentation

Une fonction est une portion de code autonome qui effectue une tâche spécifique ou un ensemble de tâches connexes. Les fonctions sont utilisées pour organiser et réutiliser du code, améliorer la lisibilité et faciliter la maintenance et le débogage d'un programme.

Appeler une Fonction

Durant les chapitres précédents, nous avons utilisé quelques fonctions prédéfinies comme : prompt(), parseInt(), isNaN(), etc. Lorsque nous appelons ces fonctions, nous suivons une structure commune : nous spécifions le nom de la fonction souhaitée, suivi de parenthèses.

Dans certains cas, les parenthèses peuvent être laissées vides, tandis que dans d'autres cas, il est nécessaire d'y fournir des valeurs, appelées "arguments". La nécessité d'utiliser des arguments dépend de la manière dont la fonction a été conçue, certains arguments étant facultatifs, tandis que d'autres sont obligatoires.

Structure et Déclaration de Fonctions

Avant d'explorer le processus de création d'une fonction personnalisée, il est important d'aborder leur nomenclature :

  • Le nom d'une fonction commence généralement par un verbe qui décrit l'action.
  • Il est recommandé d'utiliser le format camelCase (Le premier mot est en minuscule, et les mots suivants commencent par une majuscule).
  • Il faut éviter les noms trop vagues ou génériques. Ils doivent indiquer clairement ce que fait la fonction, tout en restant aussi court que possible.
  • Évitez de mélanger les langues : toutes les fonctions doivent être nommées dans une seule et même langue pour assurer la cohérence. Voici quelques exemples :
    • ajouterUnFilm()
    • effacerFichier()
    • selectionnerTousLesUtilisateurs()
    • ...
  • Lorsqu'une fonction retourne un booléen (true ou false), elles doivent être nommées de manière à indiquer qu'elles renvoient une réponse affirmative ou négative. Voici quelques exemples :
    • estValide()
    • aDesErreurs()
    • doitSeReconnecter()
    • ...

Pour créer une fonction personnalisée, la déclaration classique se fait à l'aide du mot-clé function suivi du nom que l'on désire attribuer à la fonction. Déclarons une fonction dont l'objectif sera d'afficher un petit "Hello World!" des familles :


                    // Déclaration d'une fonction nommée "direBonjourAuMonde()" permettant d'afficher "Hello World" :
                    function direBonjourAuMonde() 
                    {
                        console.log('Hello World!');
                    }

                    // Appel de la fonction "direBonjourAuMonde()".
                    direBonjourAuMonde(); // Affiche : Hello World!
                

Avec cette approche, il est possible d'appeler la fonction avant même de la déclarer, car les fonctions déclarées de cette manière sont les premiers éléments lus par JavaScript. Cependant, il est fortement conseillé de déclarer les fonctions en amont, avant de les utiliser. Cela garantit un code plus lisible, maintenable et moins sujet à des erreurs.

Paramètres et Arguments

Avec ce type de déclaration, nous sommes limités. En effet, lorsqu'on utilise une fonction, il est souvent nécessaire de lui passer des informations à traiter (comme un nombre, une chaîne de caractères, un tableau, etc.). Pour ce faire, on utilise des paramètres dans la déclaration de la fonction et des arguments lors de son appel.

  • Les paramètres : Les paramètres doivent être définis entre les parenthèses de la fonction lors de sa déclaration. Il s'agit en fait de variables qui vont récupérer les valeurs ou les références passées en argument lors de l'appel de la fonction.
  • Les arguments : Les arguments doivent être passés entre les parenthèses de la fonction lorsqu'elle est appelée.

Lorsqu'une fonction est appelée, chaque argument passé est associé de manière linéaire au paramètre correspondant : le premier argument au premier paramètre, le deuxième argument au deuxième paramètre, et ainsi de suite.


                    // Déclaration d'une fonction avec autant de paramètres d'informations provenant de l'exterieur à lui transmettre :
                    function nomDeLaFonction(parametre1, parametre2, ...)
                    {
                        /* 
                            Faire quelque chose avec les paramètres...
                            ...
                            ...
                        */
                    }

                    // Appel de la fonction.
                    nomDeLaFonction(argument1, argument2, ...);
                

Voici un exemple concret de déclaration et d'appel d'une fonction personnalisée qui affiche le double de la valeur qui lui est passée en argument :


                    // Définition d'une fonction nommée "afficherLeDouble()" permettant d'afficher le double de la valeur reçue en paramètre "b" :
                    function afficherLeDouble(b) 
                    {
                        // Utilisation de l'opérateur de multiplication et d'affectation sur la variable "b" pour multiplier sa valeur par 2.
                        b *= 2;

                        // Afficher le double de la valeur de "b".
                        console.log('Résultat : ' + b);
                    }

                    // Déclaration et initialisation de la variable "$a" avec un entier qui vaut 5.
                    const a = 5;

                    // Appel de la fonction "afficherLeDouble()" en transmettant la valeur de "a" en tant qu'argument.
                    afficherLeDouble(a); // Affiche : Valeur de b : 10
                

Il est important de noter qu'en fonction de leur nature, les arguments peuvent être passés de deux manières différentes aux fonctions :

  • Par valeur : Les types primitifs (entiers (int), flottants (float), chaînes de caractères (string), booléens (bool), NULL) sont passés par valeur. Cela signifie que lorsqu'ils sont passés en argument à une fonction, une copie de leur valeur est transmise. Par conséquent, si la valeur est modifiée à l'intérieur de la fonction, la variable d'origine ne sera pas modifiée.
  • Par référence : Les types objets (tableaux, objets littéraux, objets instanciés, etc.) sont passés par référence. Cela signifie que lorsqu'ils sont passés en argument à une fonction, si des modifications sont apportées à l'objet au sein de la fonction, la variable d'origine sera elle aussi modifiée.

Dans l'exemple que nous venons de voir, la variable a est un entier qui vaut 5. Lors de l'appel de la fonction, elle sera donc passée en argument par valeur (en tant que copie). Ce que signifie que la multiplication par deux réalisée dans la fonction n'aura aucune incidence sur la valeur de a :


                    function afficherLeDouble(b) 
                    {
                        // Utilisation de l'opérateur de multiplication et d'affectation sur la variable "b" pour multiplier sa valeur par 2.
                        b *= 2;

                        // Afficher le double de la valeur de "b".
                        console.log('Résultat : ' + b);
                    }

                    // Déclaration et initialisation de la variable "$a" avec un entier qui vaut 5.
                    const a = 5;

                    // Appel de la fonction "afficherLeDouble()" en transmettant la valeur de "a" en tant qu'argument.
                    afficherLeDouble(a); // Affiche : Valeur de b : 10

                    // Bien que "$a" ait été utilisé comme argument dans la fonction "afficherLeDouble()" et que la valeur de l'argument ait été doublée à l'intérieur de la fonction, la valeur initiale de "$a" reste inchangée.
                    console.log('Valeur de a :' + a); // Affiche : Valeur de a : 5
                

Cependant, pour les objets, la fonction reçoit une référence à l'objet, ce qui signifie que les modifications apportées à l'objet à l'intérieur de la fonction seront reflétées à l'extérieur de la fonction :


                    function afficherLeDoubleProprieteValeur(b) 
                    {
                        b.valeur *= 2;
                        console.log('Valeur de "b.valeur" : ' + b.valeur);
                    }

                    const a = { valeur: 5};
                    afficherLeDoubleProprieteValeur(a); // Affiche : Valeur de "b.valeur" : 10

                    // La valeur de la propriété "a.valeur" a bien été modifiée, car la fonction opère sur une référence à l'objet.
                    console.log('Valeur de "a.valeur" : ' + a.valeur); // Affiche : Valeur de "a.valeur" : 10
                

Les tableaux sont également passés par référence. Ainsi, toute modification effectuée directement sur le tableau à l'intérieur de la fonction impactera le tableau d'origine :


                    function doublerValeurElementsTableau(tableau) 
                    {
                        for (let i = 0; i < tableau.length; i++)
                        {
                            tableau[i] *= 2;
                        }
                    }

                    const nombres = [ 1, 2, 3, 4, 5];
                    doublerValeurElementsTableau(nombres); // Affiche : []

                    // Les éléments du tableau d'origine ont été modifié car la fonction opère sur une référence du tableau.
                    console.log(nombres); // Affiche : [ 2, 4, 6, 8, 10 ]
                

Il est possible d'assigner une valeur par défaut à un paramètre lorsqu'il est facultatif. Ainsi, si l'utilisateur ne fournit pas d'argument pour ce paramètre, la valeur par défaut sera utilisée automatiquement :


                    // Si l'utilisateur ne passe pas d'argument au paramètre facultatif "prenom",
                    // la valeur par défaut 'inconnu(e)' lui sera automatiquement attribuée.
                    function direBonjour(prenom = 'inconnu(e)')
                    {
                        console.log(`Bonjour cher(e) ${prenom}!`);
                    }

                    // Appel de la fonction "direBonjour()" sans transmettre l'argument facultatif.
                    direBonjour(); // Affiche : Bonjour cher(e) inconnu(e)!
                

Jusqu'à présent, nous avons travaillé avec des fonctions ayant un seul paramètre. Cependant, il est tout à fait possible d'utiliser des fonctions avec plusieurs paramètres. Pour illustrer cela, supposons que nous souhaitons créer une fonction qui calcule et affiche le périmètre d'un rectangle :


                    // Calcule et affiche le périmètre d'un rectangle.
                    function afficherPerimetreRectangle(longueur, largeur) 
                    {
                        // Utilisation de la formule pour calculer le périmètre.
                        const perimetre = 2 * (longueur + largeur);

                        // Affiche le résultat.
                        console.log('Le périmètre du rectangle est : ' + perimetre);
                    }

                    // Déclaration et initialisation des variables qui seront passées en argument à la fonction "afficherPerimetreRectangle()" :
                    const longueurRectangle = 5.0;
                    const largeurRectangle = 3.0;

                    // Appel de la fonction pour afficher le périmètre du rectangle.
                    afficherPerimetreRectangle(longueurRectangle, largeurRectangle); // Affiche : Le périmètre du rectangle est : 16
                

Il est tout à fait possible d'utiliser une fonction sans connaître à l'avance le nombre d'arguments qu'elle recevra. Cette flexibilité est permise grâce à l'opérateur de déversement ..., qui permet de traiter un nombre variable d'arguments.

Pour illustrer cette idée, imaginons que nous souhaitons créer une fonction qui calcule et affiche la somme de plusieurs nombres, sans utiliser un tableau comme argument, tout en permettant que le nombre de valeurs à calculer puisse varier d'un appel à l'autre :


                        // Calcule et affiche la somme de plusieurs valeurs (le nombre de valeurs passées en argument est indéfini).
                        function afficherSomme(...valeurs) 
                        {
                            // Initialisation de somme.
                            let somme = 0;

                            // Parcourir les arguments.
                            for (const valeur of valeurs)
                            {
                                somme += valeur;
                            }

                            // Afficher la somme.
                            console.log('Somme : ' + somme);
                        }

                        // Appels de la fonction avec différents quantités de nombres :
                        afficherSomme(15, 5, 30); // Affiche : Somme : 50
                        afficherSomme(10, 20); // Affiche : Somme : 30
                    

Grâce à l'utilisation de l'opérateur de déversement, cette fonction peut accepter un nombre variable d'arguments, et tous ces arguments sont rassemblés dans un tableau appelé valeurs à l'intérieur de la fonction.

En revanche, si vous appelez une fonction en préfixant un tableau passé en argument avec l'opérateur de décomposition ..., la fonction ne recevra pas un tableau, mais plutôt autant d'arguments qu'il y avait d'éléments dans le tableau.


                        // Calcule et affiche la somme de 2 valeurs.
                        function afficherSommeDeDeuxNombres(valeur1, valeur2) 
                        {
                            // Calculer la somme des deux valeurs passées en argument.
                            const somme = valeur1 + valeur2;

                            // Afficher la somme.
                            console.log('Somme : ' + somme);
                        }

                        // Déclarer la constante "valeurs" avec un tableau comprenant les deux nombres que l'on désire additionner.
                        const valeurs = [2, 5];

                        // Appel de la fonction en prefixant un tableau "valeurs" passé en argument avec l'opérateur de décomposition.
                        afficherSommeDeDeuxNombres(...valeurs); // Affiche : Somme : 7
                    

Portée des Variables dans les Fonctions

En JavaScript, lorsqu'une variable est déclarée dans une portée, elle est accessible dans tous les blocs d'instructions des portées enfants, y compris au sein des fonctions


                    // Déclaration et initialisation de la variable "a" avec un entier qui vaut 5.
                    const a = 5;

                    function afficherLeDouble() 
                    {
                        // Déclaration et initialisation de la variable "b"
                        // avec le double de la valeur de "a".
                        const b = a * 2;

                        // Afficher le double de la valeur de "a".
                        console.log('Valeur de b : ' + b);
                    }

                    // Appel de la fonction "afficherLeDouble()".
                    afficherLeDouble(); // Affiche : 10
                

Cependant, il est déconseillé de pratiquer de la sorte, car cela réduit la réutilisabilité et la testabilité de la fonction. Il est préférable de passer les valeurs nécessaires en arguments, ce qui rend la fonction plus autonome, claire et moins sujette aux erreurs.

Bien qu'une fonction puisse accéder aux éléments déclarés dans la portée parent, les éléments déclarées à l'intérieur de la fonction ne sont accessibles qu'au sain de son bloc d'instructions.

Dans l'exemple suivant, nous déclarons une fonction qui calcule le double d'une valeur mais qui ne l'affiche plus elle même. On aimerait pouvoir utiliser le résultat en dehors de la fonction pour lui faire subir toutes sortes de manipulations :


                    function calculerLeDouble(valeur) 
                    {
                        // Initialiser la valeur de "resultat" avec le double de la valeur "valeur".
                        const resultat = valeur * 2;
                    }

                    // Appel de la fonction "calculerLeDouble()" avec l'entier 5 en argument.
                    calculerLeDouble(5);

                    // On tente d'afficher la valeur de la variable $resultat. Cependant, cette tentative renverra une erreur
                    // car la portée de la variable est limitée au bloc dans lequel elle a été déclarée dans ce cas-ci "calculerLeDouble()".
                    console.log(resultat); // Affiche : Uncaught ReferenceError: resultat is not defined
                

La variable resultat est déclarée localement à l'intérieur du bloc d'instructions de la fonction calculerLeDouble(). Cela signifie qu'elle n'existe que dans le contexte de cette fonction. Si vous essayez d'accéder à cette variable en dehors de la fonction ou en dehors du bloc de code où elle est déclarée, vous obtiendrez une erreur indiquant que la variable n'est pas définie.

La portée des variables détermine où une variable est accessible et utilisable dans votre code. En d'autres termes, elle définit la zone ou le contexte dans lequel une variable existe. Dans notre exemple, la variable resultat existe uniquement à l'intérieur de la fonction calculerLeDouble(). Une fois que vous sortez de cette fonction, la variable cesse d'exister et ne peut plus être utilisée.

Si vous souhaitez utiliser la valeur d'une variable à l'extérieur d'une fonction, vous devez utiliser l'instruction return suivie de la valeur que vous souhaitez renvoyer en dehors du bloc de la fonction. Lorsqu'un return est exécuté, la fonction s'arrête et toutes les instructions situées après le return ne seront pas exécutées.


                    function calculerLeDouble(valeur) 
                    {
                        // retourne le double de la valeur valeur.
                        return valeur * 2;
                    }

                    // Appel de la fonction calculerLeDouble() avec l'entier 5 en argument. La variable resultat va récupérer la valeur située à droite de l'instruction return présente dans la fonction calculerLeDouble().
                    const resultat = calculerLeDouble(5);

                    // Afficher la valeur qui a été retournée par la fonction calculerLeDouble() et placée dans la variable resultat.
                    console.log(resultat); // Affiche : 10
                

En Javascript, pour retourner plusieurs valeurs d'une fonction, les structures les plus couramment utilisées sont les tableaux et les objets littéraux.

Voici un exemple d'une fonction qui retourne des valeurs à l'aide d'un tableau :


                    function recupererPersonnages() 
                    {
                        const lePhotographe = 'Claudy';
                        const laCommissaire = 'Laurence';

                        // Retourner plusieurs valeurs à l'aide d'un tableau.
                        return [lePhotographe, laCommissaire];
                    }

                    // Déclaration et initialisation de la variable "personnages" avec
                    // le résultat retourné par la fonction "recupererPersonnages()".
                    const personnages = recupererPersonnages();

                    // Afficher les personnages :
                    console.log(personnages); // Affiche : Array [ "Claudy", "Laurence" ]
                

Voici un exemple d'une fonction qui retourne des valeurs à l'aide d'un objet :


                    function recupererPersonnages() 
                    {
                        const lePhotographe = 'Claudy';
                        const laCommissaire = 'Laurence';

                        // Retourner plusieurs valeurs à l'aide d'un objet.
                        return {
                            lePhotographe: lePhotographe,
                            laCommissaire: laCommissaire
                        };
                    }

                    // Déclaration et initialisation de la variable "personnages" avec
                    // le résultat retourné par la fonction "recupererPersonnages()".
                    const personnages = recupererPersonnages();

                    // Afficher les personnages :
                    console.log(personnages); // Affiche : Object { lePhotographe: "Claudy", laCommissaire: "Laurence" }
                

Si vous souhaitez travailler directement avec des variables plutôt qu'avec un tableau, vous pouvez utiliser la déstructuration. La déstructuration de tableau permet d'extraire les éléments d'un tableau et de les assigner à des variables de manière concise, en utilisant la syntaxe des crochets []. Cela simplifie l'accès aux éléments individuels et facilite leur manipulation :


                        function recupererPersonnages() 
                        {
                            const lePhotographe = 'Claudy';
                            const laCommissaire = 'Laurence';

                            // Retourner plusieurs valeurs à l'aide d'un tableau.
                            return [lePhotographe, laCommissaire];
                        }

                        // Déclaration et initialisation de la variable "personnages" avec
                        // le résultat retourné par la fonction "recupererPersonnages()".
                        // Déstructuration du tableau retourné sous forme de variables individuelles.
                        const [photographe, commissaire] = recupererPersonnages();

                        // Afficher les personnages :
                        console.log(photographe); // Affiche : Claudy
                        console.log(commissaire); // Affiche : Laurence
                    

Il est également possible d'utiliser la déstructuration avec les objets. Dans ce cas, vous devez spécifier les noms des propriétés que vous souhaitez extraire, en utilisant la syntaxe des accolades {}.


                        function recupererPersonnages() 
                        {
                            const lePhotographe = 'Claudy';
                            const laCommissaire = 'Laurence';

                            // Retourner plusieurs valeurs à l'aide d'un objet.
                            return {
                                lePhotographe: lePhotographe,
                                laCommissaire: laCommissaire
                            };
                        }

                        // Déclaration et initialisation de la variable "personnages" avec
                        // le résultat retourné par la fonction "recupererPersonnages()".
                        // Déstructuration de l'objet retourné sous forme de variables individuelles.
                        const {lePhotographe, laCommissaire} = recupererPersonnages();

                        // Afficher les personnages :
                        console.log(lePhotographe); // Affiche : Claudy
                        console.log(laCommissaire); // Affiche : Laurence
                    

Exercices - Part 1

Exo-fonctions-01

Objectif : : Créer une fonction permettant d'exécuter le jeu du nombre mystère.

Instructions : :

  1. Créez un fichier nommé exo-fonctions-01.js.
  2. Copier le code à compléter dans votre éditeur de code :
    
                                function jouerAuNombreMystere()
                                {
                                    /*
                                        À COMPLÉTER : 
                                            1. Copier le code du nombre mystère.
                                    */
                                }
    
                                // Appeler la fonction permettant de lancer la partie.
                                jouerAuNombreMystere();
                            
    • Si vous avez réalisé l'exercice du nombre mystère lors du chapitre sur les boucles, vous pouvez le récupérer le code et l'adapter.
    • Sinon, commencer par réaliser l'exercice du nombre mystère dont l'énoncé est disponible à l'adresse suivant : Boucles > exercices - part 1 > exo-boucles-5.
  3. Testez la fonction avant de passer à l'exercice suivant. Pour vous assurez que l'application fonctionne bien, n'hésitez pas à ajouter un console.log() du nombre mystère lors de vos tests.

Exo-fonctions-02

Objectif : : Modifier la fonction du jeu du nombre mystère afin de permettre, lors de son appel, la configuration des valeurs minimale et maximale que le nombre mystère peut prendre.

Instructions : :

  1. Créez un fichier nommé exo-fonctions-02.js.
  2. Copiez le code à compléter dans votre éditeur de code :
    
                                /*
                                    À COMPLÉTER : 
                                        1. Copiez la fonction "jouerAuNombreMystere()" réalisée à l'exercice précédent.
                                        2. Ajoutez-lui ses paramètres : "nombreMin" et "nombreMax".
                                        3. Actualisez son bloc d'instructions pour qu'il utilise les paramètres.
                                */
    
                                // Appeler la fonction pour lancer la partie en définissant 
                                // 10 comme valeur minimale et 30 comme valeur maximale pour le nombre mystère
                                jouerAuNombreMystere(10, 30);
                            
  3. Copiez-y le code de la fonction réalisée à l'exercice précédent.
  4. Ajouter deux paramètres, nombreMin et nombreMax dans la déclaration de la fonction.
  5. Adapter l'instruction permettant d'obtenir un nombre aléatoire pour qu'elle soit dynamique à partir des deux paramètres que vous venez de créer.
  6. Adapter le texte invitant l'utilisateur à entrer un nombre, de manière à ce qu'il affiche les valeurs minimales et maximales définies par les paramètres que vous avez créés.
  7. Testez la fonction avant de passer à l'exercice suivant. Pour vous assurez que l'application fonctionne bien, n'hésitez pas à ajouter un console.log() du nombre mystère lors de vos tests.

Exo-fonctions-03

Objectif : : Créer une fonction qui effectue le tirage du jeu du lotto. Cette fonction doit générer un tableau contenant 7 numéros tirés aléatoirement, chacun compris entre 1 et 45.

Instructions : :

  1. Créez un fichier nommé exo-fonctions-03.js.
  2. Copiez le code à compléter dans votre éditeur de code :
    
                                /*
                                    À COMPLÉTER : 
                                        1. Déclarez la fonction "afficherTirageDuLotto()".
                                        2. Définissez son bloc d'instructions :
                                            2.1. Déclaration du tableau "resultatDuTirage".
                                            2.2. Boucle ( 7X ) :
                                                2.2.1 générez un nombre aléatoire
                                                2.2.2 ajoutez le nombre aléatoire au tableau "resultatDuTirage".
                                            2.3. Affichez le résultat du tirage dans la console.
                                */
    
                                // Appeler la fonction "afficherTirageDuLotto()".
                                afficherTirageDuLotto();
                            
  3. Déclarez votre fonction afficherTirageDuLotto().

    Complétez son bloc d'instructions :
    • Déclarer un tableau resultatDuTirage qui peremettra de stocker les 7 nombres générés aléatoriement.
    • Réalisez une boucle permettant avec 7 itérations qui :
      • Génère un nombre aléatoire.
      • Stocke ce nombre aléatoire dans le tableau resultatDuTirage
    • Affichez le résultat du tirage dans la console.
  4. Testez la fonction avant de passer à l'exercice suivant.

Exo-fonctions-04

Objectif : : Modifier la fonction du tirage du lotto afin de permettre, lors de son appel, la configuration de la quantité de nombres aléatoires à tirer.

Instructions : :

  1. Créez un fichier nommé exo-fonctions-04.js.
  2. Copiez le code à compléter dans votre éditeur de code :
    
                                /*
                                    À COMPLÉTER : 
                                        1. Copiez la fonction "afficherTirageDuLotto()" actualisé à l'exercice précédent.
                                        2. Ajoutez-lui le paramètre : "quantiteDeNombres".
                                        3. Actualisez la condition de la boucle du bloc d'instructions pour qu'elle utilise le paramètre.
                                */
    
                                // Appeler la fonction "afficherTirageDuLotto()".
                                afficherTirageDuLotto(10);
                            
  3. Copiez-y la fonction actualisée à l'exercice précédent.
  4. Ajouter le paramètre, quantiteDeNombres dans la déclaration de la fonction.
  5. Adapter la condition de la boucle pour qu'elle tourne autant de fois que la valeur de quantiteDeNombres.
  6. Testez la fonction avant de passer à l'exercice suivant.

Exo-fonctions-05

Objectif : : Modifier la fonction du tirage du lotto afin de permettre, lors de son appel, la configuration des valeurs minimale et maximale que les nombres aléatoires peuvent prendre.

Instructions : :

  1. Créez un fichier nommé exo-fonctions-05.js.
  2. Copiez le code à compléter dans votre éditeur de code :
    
                                /*
                                    À COMPLÉTER : 
                                        1. Copiez la fonction "afficherTirageDuLotto()" actualisé à l'exercice précédent.
                                        2. Ajoutez-lui les paramètres : "valeurMin" et "valeurMax".
                                        3. Actualisez son bloc d'instructions pour qu'il utilise les paramètres.
                                */
    
                                // Appeler la fonction "afficherTirageDuLotto()".
                                afficherTirageDuLotto(10, 1, 20);
                            
  3. Copiez-y la fonction actualisée à l'exercice précédent.
  4. Ajouter deux paramètres, valeurMin et valeurMax dans la déclaration de la fonction.
  5. Adapter l'instruction permettant d'obtenir un nombre aléatoire pour qu'elle soit dynamique à partir des deux paramètres que vous venez de créer.
  6. Testez la fonction avant de passer à l'exercice suivant.

Exo-fonctions-06

Objectif : : Modifier la fonction du tirage du lotto afin que le résultat du tirage du lotto ne puisse pas être composé de deux valeurs identiques.

Instructions : :

  1. Créez un fichier nommé exo-fonctions-06.js.
  2. Copiez le code à compléter dans votre éditeur de code :
    
                                /*
                                    À COMPLÉTER : 
                                        1. Copiez la fonction "afficherTirageDuLotto()" actualisé à l'exercice précédent.
                                        2. Actualisez son bloc d'instructions pour éviter que le tableau "resultatDuTirage" puisse contenir des doublons.
                                           Pour vérifier si une valeur est présente dans un tableau, vous pouvez vous servir de la méthode prédéfinie : "includes"
                                */
    
                                // Appeler la fonction "afficherTirageDuLotto()".
                                afficherTirageDuLotto(10, 1, 20);
                            
  3. Copiez-y la fonction actualisée à l'exercice précédent.
  4. Actualisez son bloc d'instructions pour éviter que le tableau resultatDuTirage puisse contenir des doublons. Pour vérifier si une valeur est présente dans un tableau, vous pouvez vous servir de la méthode prédéfinie : includes
  5. Testez la fonction avant de passer à l'exercice suivant.

Fonctions anonymes

Les fonctions anonymes, sont des fonctions sans nom qui peuvent s'avérer utiles dans divers contextes. Elles peuvent être assignées à des variables ou constantes pour être réutilisées ultérieurement. On les retrouve aussi souvent comme fonctions de rappel (callbacks), où elles sont passées en argument à d'autres fonctions pour exécuter du code à un moment précis. Enfin, elles sont idéales pour l'autoinvocation, une technique qui permet de créer une fonction et de l'exécuter immédiatement, ce qui limite l'impact sur l'espace global et favorise un code encapsulé.

Voici un exemple de fonction anonyme assignée à une variable. Contrairement aux fonctions déclarées de manière classique, une fonction anonyme assignée doit être définie avant d'être appelée. Sa portée dépend de la portée de la variable à laquelle elle est attachée. Enfin, la fonction peut être libérée de la mémoire en assignant null à la variable.


                    // Déclaration d'une fonction permettant de calculer et d'afficher la somme de deux valeurs passées en argument :
                    const afficherLaSomme = function(valeur1, valeur2) 
                    {
                        // Calculer le résultat de la somme.
                        const valeurSomme = valeur1 + valeur2;

                        // Afficher le résultat de la somme.
                        console.log(valeurSomme);
                    };

                    // Appel de la fonction "afficherLaSomme()" en lui passant les valeurs 40 et 2 en argument.
                    afficherLaSomme(40, 2); // Affiche : 42

                    // Supprimer la fonction
                    afficherLaSomme = null;
                

Les fonctions anonymes peuvent aussi être utilisées en tant que fonctions auto-invoquées. Ce type de fonction est défini et exécuté immédiatement. Elles sont particulièrement utiles pour encapsuler du code et éviter de polluer l'espace global :


                    // Fonction auto-invoquées (function{...})() :
                    (function() {
                        // Déclaration d'une fonction permettant de calculer et d'afficher la somme de deux valeurs passées en argument :
                        function afficherLaSomme(valeur1, valeur2) 
                        {
                            // Calculer le résultat de la somme.
                            const valeurSomme = valeur1 + valeur2;

                            // Afficher le résultat de la somme.
                            console.log(valeurSomme);
                        };

                        // Appel de la fonction "afficherLaSomme()" en lui passant les valeurs 40 et 2 en argument.
                        afficherLaSomme(40, 2); // Affiche : 42
                    })();

                    // Il est impossible d'accéder à la fonction car elle est encapulée dans la fonctions auto-invoquées.
                    afficherLaSomme(10, 2); // Affiche : Uncaught ReferenceError: afficherLaSomme is not defined
                

Les autres cas d'utilisation, comme les fonctions fléchées et les fonctions de rappel (callbacks), sont abordés en détail dans les chapitres suivants.

Fonctions Fléchées

Une autre façon de déclarer des fonctions anonymes est d'utiliser les fonctions fléchées. Elles permettent d'écrire des fonctions plus courtes, ce qui améliore la lisibilité dans certaines situations.


                    // Déclaration d'une fonction permettant d'afficher la somme de deux valeurs.
                    const afficherLaSomme = (valeur1, valeur2) =>
                    {
                        // Calculer le résultat de la somme.
                        const valeurSomme = valeur1 + valeur2;

                        // Afficher le résultat de la somme.
                        console.log(valeurSomme);
                    }

                    // Appel de la fonction afficherLaSomme.
                    afficherLaSomme(40, 2); // Affiche 42
                

Lorsqu'une fonction fléchée reçoit un seul paramètre, les parenthèses autour de celui-ci peuvent être retirées pour simplifier l'expression.


                    // const nomDeLaFonction = parametre => { bloc d'instructions }
                    const calculerLeDouble = valeur =>
                    {
                        // Calculer le double de la valeur.
                        const valeurDouble = valeur * 2;

                        // Retourner la valeur doublée.
                        return valeurDouble;
                    }

                    // Appel de la fonction calculerLeDouble.
                    const resultat = calculerLeDouble(5);
                    console.log(resultat); // Affiche 10
                

Il est possible de réduire le bloc à une seule instruction pour simplifier la fonction.


                    const calculerLeDouble = valeur => { return valeur * 2 };

                    const resultat = calculerLeDouble(5);
                    console.log(resultat); // Affiche 10
                

Si le bloc de la fonction ne contient plus qu'une seule instruction return qui renvoie une expression simple, comme dans l'exemple précédent, on peut retirer les accolades {}. Cette expression est alors placée directement après la flèche et JavaScript la renvoie automatiquement grâce à un return implicite. Dans ce cas, il ne faut pas écrire le mot return, sinon cela provoquerait une erreur.


                    // Sans accolades, la fonction renvoie automatiquement la valeur de l'expression.
                    const calculerLeDouble = valeur => valeur * 2;

                    const resultat = calculerLeDouble(5);
                    console.log(resultat); // Affiche 10
                

Exercices : Part 2

Exo-fonctions-07

Objectif : Écrire une fonction JavaScript qui renvoie un message de statut pour un serveur en fonction de son état (en maintenance ou disponible), comme si vous prépariez un futur tableau de bord technique.

Instructions :

  1. Créez un fichier nommé exo-fonctions-07.js.
  2. Dans ce fichier, déclarez une fonction fléchée sans paramètre nommée genererMessageStatutServeur.
  3. À l'intérieur de la fonction, créez une constante estEnMaintenance initialisée avec true ou false pour simuler l'état actuel du serveur.
  4. Utilisez une structure if pour tester la valeur de estEnMaintenance. Si la valeur est vraie, la fonction renvoie un message qui indique que le serveur est en maintenance et donc indisponible. Sinon, la fonction renvoie un message qui indique que le serveur fonctionne normalement.
  5. En dehors de la fonction, appelez genererMessageStatutServeur(), stockez le résultat dans une variable (par exemple messageStatut) puis affichez cette variable dans la console avec console.log.

Exo-fonctions-08

Objectif : Écrire une fonction JavaScript qui génère un message lisible sur le nombre de tâches en attente pour un utilisateur, en prenant en compte les cas zéro, un, plusieurs et les valeurs négatives.

Instructions :

  1. Créez un fichier nommé exo-fonctions-08.js.
  2. Dans ce fichier, déclarez une fonction fléchée avec un seul paramètre nommée formaterMessageTaches. Cette fonction reçoit un nombre de tâches en attente dans un paramètre nombreTaches.
  3. À l'intérieur de la fonction, utilisez une structure if / else if / else afin de renvoyer un message adapté à la valeur de nombreTaches avec l'instruction return.
    Par exemple :
    • si nombreTaches est inférieur ou égal à 0 renvoyer un message du type "Vous n'avez aucune tâche en attente",
    • si nombreTaches vaut exactement 1 renvoyer un message du type "Vous n'avez qu'une seule tâche en attente",
    • sinon, renvoyer un message du type "Vous avez X tâches en attente" en utilisant la valeur du paramètre dans le texte.
  4. En dehors de la fonction, créez une variable messagePourUtilisateur puis affectez-lui le résultat de l'appel à formaterMessageTaches() avec différentes valeurs pour nombreTaches.
    Par exemple 0, 1, 5 et éventuellement une valeur négative.
  5. Affichez la variable messagePourUtilisateur dans la console avec console.log après chaque appel afin de vérifier que le message correspond bien au nombre de tâches testé.

Exo-fonctions-09

Objectif : Écrire un petit calculateur pour un panier d'achat en séparant le calcul du prix TTC et le calcul du total pour un article du panier.

Instructions :

  1. Créez un fichier nommé exo-fonctions-09.js.
  2. Dans ce fichier, déclarez une fonction fléchée stockée dans une constante et nommée calculerPrixTTC. Cette fonction reçoit deux paramètres prixHT (prix hors TVA) et tauxTVA (par exemple 21 pour 21 %). À l'intérieur de la fonction
    • calculez le montant de la TVA dans une variable par exemple montantTVA,
    • calculez le prix TTC dans une autre variable par exemple prixTTC,
    • renvoyez le prix TTC avec return.
  3. Déclarez ensuite une deuxième fonction fléchée stockée dans une constante et nommée calculerTotalLigne. Cette fonction reçoit trois paramètres prixUnitaireHT, quantite et tauxTVA. À l'intérieur de la fonction
    • appelez calculerPrixTTC en lui passant prixUnitaireHT et tauxTVA et stockez le résultat dans une variable par exemple prixUnitaireTTC,
    • calculez le total de la ligne dans une variable par exemple totalLigne en multipliant le prix TTC par la quantité,
    • renvoyez ce total avec return.
  4. En dehors des fonctions, créez une variable totalCommande et affectez-lui le résultat de l'appel à calculerTotalLigne avec des valeurs de test par exemple un prix unitaire HT de 100, une TVA de 21 et une quantité de 3.
  5. Affichez le contenu de totalCommande dans la console avec console.log pour vérifier que le total calculé est cohérent.

Exo-fonctions-10

Objectif : Écrire un petit outil qui reçoit un tableau de durées de tâches en minutes et qui renvoie un nouveau tableau contenant uniquement les tâches considérées comme longues par exemple à partir de 30 minutes.

Instructions :

  1. Créez un fichier nommé exo-fonctions-10.js.
  2. Au début du fichier, créez un tableau nommé dureesTaches qui contient plusieurs durées de tâches en minutes par exemple [25, 40, 15, 60, 5, 45].
  3. Déclarez une fonction fléchée stockée dans une constante et nommée estTacheLongue. Cette fonction reçoit une durée en minutes en paramètre et renvoie true si la tâche est considérée comme longue ou false sinon. Pour cet exercice, une tâche est longue si sa durée est supérieure ou égale à 30. Essayez d'écrire cette fonction de manière simple et concise.
  4. Déclarez ensuite une fonction fléchée stockée dans une constante et nommée filtrerTachesLongues. Cette fonction reçoit un tableau de durées en minutes en paramètre et doit renvoyer un nouveau tableau contenant uniquement les durées des tâches longues. À l'intérieur de la fonction
    • créez un tableau vide par exemple tachesLongues,
    • parcourez le tableau reçu en paramètre avec une boucle par exemple une boucle for,
    • pour chaque durée, appelez la fonction estTacheLongue pour savoir si vous devez la garder,
    • si la tâche est longue, ajoutez la durée au tableau tachesLongues,
    • à la fin de la boucle, renvoyez le tableau tachesLongues avec l'instruction return.
  5. En dehors des fonctions, créez une variable dureesLongues et affectez-lui le résultat de l'appel à filtrerTachesLongues(dureesTaches).
  6. Affichez dans la console le tableau d'origine dureesTaches puis le tableau filtré dureesLongues afin de vérifier que seules les tâches longues ont été conservées.

Fonctions de rappel (callbacks)

Une fonction de rappel, ou callback, désigne une fonction que l'on passe en argument à une autre fonction. La fonction appelée ne l'exécute pas directement. Elle garde cette fonction de côté et décide du moment où l'appeler.

Ce mécanisme sépare deux responsabilités :

  • le cadre de l'action, c est à dire quand exécuter le code, combien de fois, dans quel ordre
  • le contenu de l'action, c est à dire ce qu il faut faire concrètement

Une même fonction peut ainsi recevoir des callbacks différents afin de changer le comportement sans dupliquer tout le code. De nombreuses fonctionnalités modernes de JavaScript reposent sur ce principe événements du navigateur, timers, parcours de listes, requêtes réseau, etc.

Lorsqu une fonction attend un callback, on lui transmet la fonction elle-même, sans parenthèses. Par exemple, faireQuelqueChose(maFonction) transmet la fonction, tandis que faireQuelqueChose(maFonction()) exécute la fonction tout de suite et ne transmet que son résultat.

Premier exemple simple : callback sans paramètre

On commence avec un cas volontairement simple. On crée une fonction executerDeuxFois() qui reçoit une fonction de rappel et l'exécute deux fois de suite.


                    // Callback stocké dans une constante avec une fonction fléchée.
                    const afficherMessage = () =>
                    {
                        console.log('Bonjour depuis le callback');
                    };

                    // Fonction "moteur" qui reçoit une fonction de rappel
                    // et l'exécute deux fois.
                    const executerDeuxFois = (action) =>
                    {
                        console.log('Début de executerDeuxFois');
                        action(); // 1er appel du callback.
                        action(); // 2e appel du callback.
                        console.log('Fin de executerDeuxFois');
                    };

                    // Utilisation avec la fonction stockée dans "afficherMessage".
                    // On passe la fonction elle-même, sans les parenthèses.
                    executerDeuxFois(afficherMessage);
                

Déroulé pas à pas.

  • La constante afficherMessage contient une fonction fléchée qui affiche un message dans la console.
  • La constante executerDeuxFois contient une fonction fléchée qui reçoit un paramètre nommé action. Ce paramètre est prévu pour recevoir une fonction.
  • Quand le script exécute executerDeuxFois(afficherMessage), la fonction stockée dans afficherMessage est copiée dans le paramètre action.
  • À l intérieur de executerDeuxFois, l instruction action() appelle donc le callback reçu en paramètre. Comme cette ligne apparaît deux fois, le message est affiché deux fois.
  • La fonction executerDeuxFois ne connaît pas le contenu du message. Son rôle se limite à encadrer l exécution log de début, deux appels au callback, log de fin.

Ce premier exemple sert surtout à ancrer une idée clé une fonction peut recevoir une autre fonction en paramètre et l exécuter plus tard à l aide de son nom.

Pourquoi ne pas tout écrire en dur dans la fonction ?

Sans callback, la logique de transformation est souvent écrite en dur dans une fonction. Cela fonctionne tant que le besoin reste toujours le même, mais dès que l'on veut changer le traitement, il faut modifier la fonction ou en créer une nouvelle presque identique.

Exemple avec une fonction qui double toutes les valeurs d'un tableau.


                    // Version rigide : la fonction est spécialisée pour le "x2".
                    function doublerToutesLesValeurs(listeDeValeurs)
                    {
                        const tableauDeSortie = [];

                        for (const valeur of listeDeValeurs)
                        {
                            const valeurDoublee = valeur * 2;
                            tableauDeSortie.push(valeurDoublee);
                        }

                        return tableauDeSortie;
                    }

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

                    const resultats = doublerToutesLesValeurs(nombres);

                    console.log(resultats); // Affiche : Array(5) [ 2, 4, 6, 8, 10 ]
                

Si l'on souhaite plus tard tripler les nombres, les mettre au carré ou fabriquer un libellé texte, on se retrouve à recopier la même boucle avec seulement une ligne différente à l'intérieur.

Avec un callback, on sépare le parcours du tableau et le traitement appliqué à chaque valeur. La fonction centrale s'occupe de la structure et délègue le calcul à une fonction de rappel.


                    // Callback responsable du "x2".
                    function doublerEntier(valeur)
                    {
                        return valeur * 2;
                    }

                    // Fonction "moteur" qui applique une fonction de rappel à chaque élément.
                    function appliquerSurChaqueElement(fonctionDeRappel, listeDeValeurs)
                    {
                        const tableauDeSortie = [];

                        for (const valeur of listeDeValeurs)
                        {
                            const valeurTransformee = fonctionDeRappel(valeur);
                            tableauDeSortie.push(valeurTransformee);
                        }

                        return tableauDeSortie;
                    }

                    // Initialiser "nombres" avec un tableau d'entiers.
                    const nombres = [1, 2, 3, 4, 5];

                    // Appliquer la fonction de rappel "doublerEntier()" sur chaque élément du tableau.
                    const resultats = appliquerSurChaqueElement(doublerEntier, nombres);

                    console.log(resultats); // Affiche : Array(5) [ 2, 4, 6, 8, 10 ]
                

Analyse rapide.

  • appliquerSurChaqueElement() parcourt le tableau listeDeValeurs, appelle la fonction de rappel pour chaque valeur, et stocke les résultats dans un nouveau tableau.
  • La ligne const valeurTransformee = fonctionDeRappel(valeur) montre clairement comment la fonction de rappel sera utilisée. À chaque tour de boucle, elle reçoit la valeur courante du tableau et doit renvoyer une version transformée. Toute fonction passée en callback doit suivre ce fonctionnement pour que le reste du code puisse l'utiliser sans modification.
  • Lors de l'appel appliquerSurChaqueElement(doublerEntier, nombres), le paramètre fonctionDeRappel reçoit la fonction doublerEntier (sans parenthèses). À chaque tour de boucle, c'est donc doublerEntier(valeur) qui est exécutée à la place de fonctionDeRappel(valeur).

Les callbacks prennent tout leur sens dès que l'on souhaite appliquer un traitement différent aux éléments du tableau sans modifier ni réécrire la fonction qui s'occupe de le parcourir.


                    // Autre callback : calculer le tiers avec 2 décimales.
                    function calculerTiersAvecDeuxDecimales(valeur)
                    {
                        const resultat = valeur / 3;
                        
                        // Retourne une chaîne représentant le nombre arrondi.
                        // toFixed(2) produit un texte avec exactement 2 chiffres après la virgule.
                        return resultat.toFixed(2);
                    }

                    // Fonction "moteur" qui applique une fonction de rappel à chaque élément.
                    // Elle est identique à celle de l'exemple précédent.
                    function appliquerSurChaqueElement(fonctionDeRappel, listeDeValeurs)
                    {
                        const tableauDeSortie = [];

                        for (const valeur of listeDeValeurs)
                        {
                            const valeurTransformee = fonctionDeRappel(valeur);
                            tableauDeSortie.push(valeurTransformee);
                        }

                        return tableauDeSortie;
                    }

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

                    const tiersArrondis = appliquerSurChaqueElement(calculerTiersAvecDeuxDecimales, nombres);

                    console.log(tiersArrondis); // Affiche : [ "0.33", "0.67", "1.00", "1.33", "1.67" ]
                

Le même tableau nombres est traité par appliquerSurChaqueElement(), mais le résultat change selon la fonction de rappel utilisée.

  • Avec doublerEntier passé en callback, on obtenait un nouveau tableau contenant les valeurs du tableau d'origine multipliées par deux.
  • Avec calculerTiersAvecDeuxDecimales passé en callback, on obtient un nouveau tableau contenant les tiers des valeurs du tableau d'origine, arrondis à deux décimales.
  • La fonction appliquerSurChaqueElement() reste identique. Seule la fonction de rappel change selon le traitement souhaité.

Déclarer une fonction de rappel anonyme directement en argument

Jusqu ici, les fonctions de rappel étaient toujours stockées dans une constante avant d être transmises à une autre fonction. Cette approche reste une très bonne habitude pour les traitements importants ou réutilisables.

Il est aussi possible de déclarer une fonction fléchée anonyme directement au moment de l appel. Autrement dit, on écrit la fonction à l intérieur des parenthèses de l appel, sans lui donner de nom.


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

                    function appliquerSurChaqueElement(fonctionDeRappel, listeDeValeurs)
                    {
                        const tableauDeSortie = [];

                        for (const valeur of listeDeValeurs)
                        {
                            const valeurTransformee = fonctionDeRappel(valeur);
                            tableauDeSortie.push(valeurTransformee);
                        }

                        return tableauDeSortie;
                    }

                    // Utilisation avec une fonction de rappel anonyme.
                    const resultat = appliquerSurChaqueElement((valeur) =>
                    {
                        return valeur * 10;
                    }, nombres);

                    console.log(resultat); // [10, 20, 30, 40, 50]
                

Dans cet exemple, la fonction fléchée anonyme (valeur) => { return valeur * 10; } n est définie que pour cet appel précis. Elle ne porte pas de nom et n est pas stockée dans une constante.

Quand utiliser une fonction de rappel anonyme

  • Quand le traitement est court et simple, par exemple une petite transformation comme valeur * 10 ou un simple test.
  • Quand la fonction ne sera utilisée qu une seule fois dans le script.
  • Quand créer une constante séparée rendrait le code plus long sans réel bénéfice en lisibilité.

Quand il vaut mieux stocker le callback dans une constante

  • Quand la logique tient plusieurs lignes et mérite un nom clair, par exemple doublerEntier ou calculerTiersAvecDeuxDecimales.
  • Quand le même traitement doit être réutilisé à plusieurs endroits du script.
  • Quand on veut faciliter la relecture, les tests ou la documentation de la fonction de rappel.

En pratique, on choisit une fonction de rappel anonyme quand l action est ponctuelle et très simple, et une fonction nommée stockée dans une constante quand le traitement est important, réutilisable ou mérite une explication explicite par son nom.

Exercices - Part 3

Exo-fonctions-11

Objectif : Mettre en place une fonction générale qui applique une opération sur deux nombres, puis réutiliser cette fonction avec plusieurs fonctions fléchées différentes comme dans un petit module de calcul.

Instructions :

  1. Créez un fichier nommé exo-fonctions-11.js.
  2. Déclarez une constante nommée appliquerOperationSurDeuxNombres qui contient une fonction fléchée avec trois paramètres a, b et operation. Le bloc de cette fonction devra
    • appeler la fonction de rappel operation avec a et b,
    • stocker le résultat dans une constante resultat,
    • afficher dans la console un message du type "Résultat de l opération : X" en remplaçant X par la valeur de resultat.
  3. Déclarez ensuite trois fonctions fléchées stockées dans des constantes addition, soustraction et moyenne. Chaque fonction doit recevoir deux nombres en paramètres et renvoyer le résultat de l opération correspondante par exemple la somme pour addition, la différence pour soustraction et la moyenne des deux valeurs pour moyenne.
  4. Appelez plusieurs fois appliquerOperationSurDeuxNombres en lui passant des valeurs de test par exemple 10 et 5 pour les deux premiers paramètres et l une des fonctions précédentes pour le troisième paramètre. Par exemple :
    
                                appliquerOperationSurDeuxNombres(10, 5, addition);
                                appliquerOperationSurDeuxNombres(10, 5, soustraction);
                                appliquerOperationSurDeuxNombres(10, 5, moyenne);
                            

Exo-fonctions-12

Objectif : Créer une petite fonction utilitaire qui parcourt une liste de tâches et applique une action reçue en paramètre sur chaque élément, sans utiliser map() ni filter().

Instructions :

  1. Créez un fichier nommé exo-fonctions-12.js.
  2. Déclarez un tableau taches contenant quelques tâches simples :
    
                                const taches = [
                                    'Envoyer le mail au client',
                                    'Relancer le fournisseur',
                                    'Préparer le rapport mensuel'
                                ];
                            
  3. Déclarez une constante nommée pourChaqueTache qui contient une fonction fléchée avec deux paramètres listeTaches (un tableau de chaînes) et action (une fonction de rappel). Le bloc d instructions devra
    • parcourir listeTaches avec une boucle,
    • à chaque itération, récupérer la tâche courante,
    • appeler action(tache, index) en passant la tâche et sa position dans le tableau.
  4. Déclarez une première fonction fléchée stockée dans une constante afficherTacheAvecIndex. Cette fonction reçoit une tâche et son index puis affiche dans la console une ligne composée de l'index suivi de sa tâche (ex.: "0 - Envoyer le mail au client"). Appelez ensuite pourChaqueTache(taches, afficherTacheAvecIndex).
  5. Déclarez une deuxième fonction fléchée stockée dans une constante afficherTacheSiLongue. Cette fonction reçoit une tâche et son index, vérifie si la longueur de la tâche est supérieure à 25 caractères, si c'est le cas, affiche dans la console une ligne composée de l'index suivi de sa tâche ("0 - Envoyer le mail au client"). Appelez ensuite pourChaqueTache(taches, afficherTacheSiLongue).

Exo-fonctions-13

Objectif : Utiliser des fonctions fléchées sans paramètre comme callbacks, dans une situation proche d une interface de connexion.

Instructions :

  1. Créez un fichier nommé exo-fonctions-13.js.
  2. Déclarez une constante nommée executerSiConnecte qui contient une fonction fléchée avec deux paramètres estConnecte (booléen) et actionLorsqueConnecte (fonction de rappel). Le bloc d instructions devra
    • si estConnecte vaut true, appeler actionLorsqueConnecte(),
    • sinon, afficher "Accès refusé, merci de vous connecter." dans la console.
  3. Appelez cette fonction avec une fonction fléchée sans paramètre pour simuler un utilisateur connecté :
    
                                executerSiConnecte(true, () =>
                                {
                                    console.log('Ouverture du tableau de bord...');
                                });
                            
  4. Appelez à nouveau executerSiConnecte avec false comme premier argument et une autre fonction fléchée sans paramètre pour vérifier que le message d accès refusé s affiche correctement.

Méthodes prédéfinies de tableau

Lorsque nous avons abordé la section sur les fonctions de rappel (callbacks), nous avons créé notre propre fonction appliquerSurChaqueElement() pour parcourir un tableau et transformer ses valeurs en utilisant une fonction de rappel. Ce principe est tellement courant en JavaScript que le langage propose déjà des méthodes prédéfinies pour effectuer ces opérations de manière simple, expressive et optimisée.

Ces méthodes permettent de transformer un tableau, d'en filtrer certains éléments ou d'en extraire une information, le tout en utilisant le même principe de fonction de rappel. Elles évitent d'écrire des boucles répétitives et rendent le code plus lisible.

map()

La méthode map() reprend exactement le rôle de notre fonction appliquerSurChaqueElement(). Elle parcourt le tableau d'origine, applique une fonction de rappel à chaque valeur, puis construit un nouveau tableau contenant les résultats.


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

                    function multiplierParDeux(nombre)
                    {
                        return nombre * 2;
                    }

                    // Doubler chaque nombre du tableau.
                    const nombresDoubles = nombres.map(multiplierParDeux);

                    console.log(nombresDoubles); // Affiche : [2, 4, 6, 8, 10]
                

Nous aurions aussi pu choisir une version plus concise avec une fonction fléchée. Lorsque la fonction ne comporte qu'une seule instruction, les accolades peuvent être omises. Dans ce cas, le mot-clé return est implicite et n'a pas besoin d'être écrit.


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

                    const multiplierParDeux = nombre => nombre * 2; 

                    // Doubler chaque nombre dans le tableau.
                    const nombresDoubles = nombres.map(multiplierParDeux);

                    console.log(nombresDoubles); // Affiche : [2, 4, 6, 8, 10]
                

Quand le traitement est court et n'est utilisé qu'une seule fois, il est plus pratique d'écrire la fonction de rappel directement dans l'argument, ce qui garde le code lisible et compact.


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

                    // Doubler chaque nombre dans le tableau.
                    const nombresDoubles = nombres.map(nombre => nombre * 2);

                    console.log(nombresDoubles); // Affiche : [2, 4, 6, 8, 10]
                

filter()

La méthode filter() permet de filtrer les éléments d'un tableau en fonction d'une condition définie dans une fonction de rappel. Elle crée un nouveau tableau contenant tous les éléments pour lesquels la fonction de rappel renvoie true.


                    const nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

                    // Filtrer les nombres pairs.
                    const nombresPairs = nombres.filter(nombre => nombre % 2 === 0);

                    /*
                        Valeur du filtre à chaque itération :

                                        nombre => nombre % 2 === 0   résultat : tableau de sortie
                        -------------------------------------------------------------------------
                        1ère itération :   1   =>    1   % 2 === 0      false : ignoré
                        2ème itération :   2   =>    2   % 2 === 0       true : ajouté
                        3ème itération :   3   =>    3   % 2 === 0      false : ignoré
                        ...
                    */

                    console.log(nombresPairs); // Affiche : [2, 4, 6, 8, 10]
                

reduce()

La méthode reduce() est utilisée pour accumuler les éléments d'un tableau.
Elle possède 2 paramètres :

  • 1er paramètre : Une fonction de rappel qui détermine la façon dont les éléments sont accumulés.
  • 2ème paramètre (facultatif) : La valeur initiale de l'accumulateur. Si celle-ci n'est pas précisée, la valeur initiale prendra la première valeur du tableau.
La fonction de rappel possède 2 paramètres :
  • 1er paramètre : L'accumulateur est soit la valeur retournée par l'appel précédent de la fonction de rappel, soit la valeur initiale fournie si c'est le premier appel.
  • 2ème paramètre : La valeur de l'élement actuel.

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

                    // Calculer la somme de tous les nombres dans le tableau.
                    const somme = nombres.reduce((acc, nombre) => acc + nombre, 0);

                    /*
                        Valeur de l'accumulateur à chaque itération :

                                            acc = acc + nombre   résultat
                        -----------------------------------------------
                        1ère itération : acc =  0  +   1             1
                        2ème itération : acc =  1  +   2             3
                        3ème itération : acc =  3  +   3             6
                        4ème itération : acc =  6  +   4            10
                        5ème itération : acc = 10  +   5            15
                    */

                    console.log(somme); // Affiche : 15
                

Notez que les fonctions de rappel utilisées avec les méthodes prédéfinies map(), filter() et reduce() peuvent prendre 2 paramètres facultatifs supplémentaires : L'index de l'élement actuel suivi du tableau parcouru.

Voici un exemple d'utilisation de ces deux paramètres avec la méthode prédéfinie map() permettant d'inverser l'ordre des éléments d'un tableau de manière non destructive, c'est à dire, sans modifier le tableau d'origine :


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

                        // Doubler chaque nombre dans le tableau.
                        const nombresDoubles = nombres.map((nombre, index, tableau) => tableau[tableau.length -1 -index]);

                        console.log(nombresDoubles); // Affiche : [5, 4, 3, 2, 1]
                    

sort()

La méthode sort() trie un tableau et modifie directement le tableau d'origine. Sans fonction de comparaison, JavaScript convertit les éléments en chaînes et les compare comme du texte.


                    const prenoms = ['Alice', 'Claudy', 'Bob', 'Abdelillah'];

                    // Tri alphabétique simple, sans fonction de comparaison.
                    prenoms.sort();

                    console.log(prenoms);
                    /*
                        Affiche:
                            array(4)
                            [
                                'Abdelillah',
                                'Alice',
                                'Bob',
                                'Claudy'
                            ]
                    */
                

Ce comportement convient pour un tri alphabétique simple, avec des prénoms contenant des caractères "simples" et une comparaison de chaîne basique. Dès que le tri doit devenir plus précis, par exemple des nombres ou des règles personnalisées, fournir une fonction de comparaison devient nécessaire.

Exemple classique qui montre la limite du tri par défaut. Avec des nombres, sort() compare du texte, pas des valeurs numériques. Résultat, 10 devient '10', 2 devient '2' et lorsque les deux chaînes sont comparées '10' est placé avant '2'.


                    const nombres = [9, 1, 5, 2, 3, 10, 7, 4, 6, 8];

                    // Tri par défaut, comparaison en tant que chaînes.
                    nombres.sort();

                    console.log(nombres); // Affiche: array(10) [1, 10, 2, 3, 4, 5, 6, 7, 8, 9]
                

Pour imposer un vrai tri numérique, fournir une fonction de comparaison. Elle indique à sort() comment décider quel élément doit passer avant l'autre.

La fonction de comparaison est appelée par sort() pour comparer deux éléments à la fois. À chaque appel, elle doit décider lequel des deux doit venir avant l'autre. Le nombre retourné indique l'ordre à appliquer pour cette paire d'éléments.

  • 1er paramètre Une fonction de comparaison. Elle doit retourner
    • Un nombre négatif Le premier élément passe avant le second.
    • 0 Les deux éléments sont considérés équivalents pour le tri.
    • Un nombre positif Le premier élément passe après le second.

                    const nombres = [9, 1, 5, 2, 3, 10, 7, 4, 6, 8];

                    // Tri numérique croissant.
                    nombres.sort((a, b) => a - b);

                    console.log(nombres); // Affiche: array(10) [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
                

La fonction de comparaison rend aussi le tri plus flexible. Dans l'exemple suivant, les prénoms sont triés par longueur, du plus court au plus long.


                const prenoms = ['Alice', 'Claudy', 'Bob', 'Abdelillah'];

                // Tri par longueur de chaîne.
                prenoms.sort((a, b) => a.length - b.length);

                console.log(prenoms);
                /*
                    Affiche:
                        array(4)
                        [
                            'Bob',
                            'Alice',
                            'Claudy',
                            'Abdelillah'
                        ]
                */
                

Dans certains cas, il est important de ne pas modifier le tableau d'origine, par exemple lorsqu'il représente des données de référence utilisées à plusieurs endroits du script. Comme sort() modifie toujours le tableau sur lequel elle est appelée, une pratique courante consiste à créer d'abord une copie du tableau, puis à trier uniquement cette copie.

Plusieurs techniques permettent de fabriquer un nouveau tableau avant d'appliquer sort(). Elles reposent toutes sur la même idée : dupliquer les données d'origine, puis trier la copie.

Copier le tableau avec slice()

La méthode slice() permet de créer un nouveau tableau à partir d'un tableau existant. C'est une méthode très utilisée car elle permet de :

  • dupliquer un tableau entier sans modifier l'original,
  • extraire une portion d'un tableau (par exemple les 5 premiers éléments),
  • travailler sur une copie sécurisée lorsqu'on veut éviter d'altérer les données sources.

À l'inverse, la méthode déjà vue splice() modifie le tableau d'origine (ajout, suppression ou remplacement d'éléments). slice(), elle, ne change jamais le tableau sur lequel elle est appelée. Elle renvoie toujours une nouvelle version, totalement indépendante.

Lorsqu'on souhaite trier un tableau sans toucher aux données d'origine, slice() est donc une technique simple et fiable.


                    const produits = ['Souris', 'Clavier', 'Manette'];

                    // Créer une copie indépendante puis la trier.
                    const produitsTries = produits.slice().sort();

                    console.log(produits); // Affiche: Array(3) [ "Souris", "Clavier", "Manette" ]
                    console.log(produitsTries); // Affiche: Array(3) [ "Clavier", "Manette", "Souris" ]
                

Copier le tableau avec l'opérateur de décomposition (spread ...)

L'opérateur de décomposition ... permet également de créer une copie peu verbeuse d'un tableau. Il est très utilisé dans le code moderne.


                    const prenoms = ['Marc', 'Alice', 'Nour'];

                    // Copier avec l'opérateur spread, puis trier la copie.
                    const prenomsTries = [...prenoms].sort();

                    console.log(prenoms); // Affiche: Array(3) [ "Marc", "Alice", "Nour" ]
                    console.log(prenomsTries); // Affiche: Array(3) [ "Alice", "Marc", "Nour" ]
                

Copier le tableau avec Array.from()

La méthode Array.from() construit un nouveau tableau à partir d'un autre. Elle peut également servir à dupliquer un tableau avant d'appliquer un tri ou d'autres transformations.


                    const valeurs = [30, 10, 20];

                    // Fabrication d'une copie avec Array.from() puis tri numérique croissant.
                    const valeursTries = Array.from(valeurs).sort((a, b) => a - b);

                    console.log(valeurs); // Affiche: Array(3) [ 30, 10, 20 ]
                    console.log(valeursTries); // Affiche Array(3) [ 10, 20, 30 ]
                

Utiliser toSorted() pour trier sans modifier le tableau d'origine

Depuis 2023, JavaScript propose une nouvelle méthode : toSorted(). Elle fonctionne comme sort() mais avec une différence importante, elle ne modifie jamais le tableau d'origine. Elle renvoie systématiquement une copie triée, ce qui évite d'avoir à utiliser slice(), Array.from() ou l'opérateur ....

La syntaxe est identique à celle de sort(), c'est-à-dire que vous pouvez utiliser une fonction de comparaison lorsque vous avez besoin d'un tri personnalisé.


                    const produits = ['Souris', 'Clavier', 'Manette'];

                    // Créer une copie triée automatiquement.
                    const produitsTries = produits.toSorted();

                    console.log(produits); // Affiche: Array(3) [ "Souris", "Clavier", "Manette" ]
                    console.log(produitsTries); // Affiche: [ "Clavier", "Manette", "Souris" ]
                

toSorted() accepte également une fonction de comparaison, comme sort() :


                    const articles = [
                        { 
                            nom: 'Clavier', 
                            prix: 40 
                        },
                        { 
                            nom: 'Manette', 
                            prix: 60 
                        },
                        { 
                            nom: 'Souris', 
                            prix: 15 
                        }
                    ];

                    // Tri du moins cher au plus cher, sans toucher à "articles".
                    const articlesTries = articles.toSorted(
                        (a, b) => a.prix - b.prix
                    );

                    console.log(articles);
                    /*
                        Affiche:
                            Array(3) [ {…}, {…}, {…} ]
                                0: Object { nom: "Clavier", prix: 40 }
                                1: Object { nom: "Manette", prix: 60 }
                                2: Object { nom: "Souris", prix: 15 }
                    */

                    console.log(articlesTries);
                    /*
                        Affiche:
                            Array(3) [ {…}, {…}, {…} ]
                                0: Object { nom: "Souris", prix: 15 }
                                1: Object { nom: "Clavier", prix: 40 }
                                2: Object { nom: "Manette", prix: 60 }
                    */
                

Ces quatre techniques permettent de préserver le tableau original tout en obtenant une version triée prête à être affichée dans une interface (liste d'articles, catalogue produit, tableau d'administration, etc.).

Trier avec localeCompare()

Comme nous l'avons vu précédemment, une fonction de comparaison simple peut déjà donner de bons résultats. Toutefois, une approche naïve peut aussi produire des résultats incohérents, car le comportement du tri dépend du type de valeurs comparées.

Lorsqu'un tri concerne du texte affiché dans une interface, une comparaison naïve peut produire un ordre inattendu, car elle ne prend pas en compte les conventions linguistiques comme les accents ou la casse. localeCompare() est conçu pour pallier ce type de problème.

Exemple avec du texte

                    const prenoms = ['Émile', 'Emile', 'Zoé', 'Zoe', 'Alice', 'Álvaro'];

                    // Tri par défaut (sans fonction de comparaison).
                    const triParDefaut = prenoms.toSorted();

                    // Tri avec localeCompare.
                    const triAvecLocaleCompare = prenoms.toSorted((a, b) => a.localeCompare(b));

                    console.log(triParDefaut); // Affiche: Array(6) [ "Alice", "Emile", "Zoe", "Zoé", "Álvaro", "Émile" ]
                    console.log(triAvecLocaleCompare); // Affiche: Array(6) [ "Alice", "Álvaro", "Emile", "Émile", "Zoe", "Zoé" ]
                

Le tri par défaut compare les caractères selon un ordre interne, ce qui peut placer des lettres accentuées à un endroit inattendu pour un tri de type dictionnaire. Avec localeCompare(), la comparaison se rapproche d'un ordre alphabétique plus cohérent pour une interface utilisateur.

Paramètres facultatifs de localeCompare()

Par défaut, localeCompare() compare deux chaînes selon les réglages du navigateur. Il est possible de préciser des paramètres pour obtenir un tri plus cohérent dans une interface, notamment quand on veut contrôler la langue utilisée, la gestion des accents, ou la casse.


                    texteA.localeCompare(texteB, locale, options);
                
  • locale (facultatif, défaut : locale du navigateur)
    Permet d'indiquer la langue utilisée pour comparer les chaînes. Cela influence des détails comme l'ordre alphabétique, la manière de gérer certains caractères, ou encore des règles propres à une langue, plutôt que de dépendre uniquement du réglage du navigateur.
    Ce paramètre peut être :
    • Une chaîne qui précise la langue (ex. 'fr', 'fr-BE', 'en').
    • Un tableau de locales pour proposer des alternatives si une locale n'est pas disponible (ex. ['fr-BE', 'fr', 'en']).
  • options (facultatif)
    Permet de préciser les règles de comparaison. Il s'agit d'un objet pouvant contenir les propriétés suivantes :
    • sensitivity (facultatif, défaut : 'variant')
      Définir quelles différences doivent être prises en compte entre deux textes, en particulier la casse et les accents.
      • 'base' : Ignorer casse et accents.
        Exemple : a, A, á peuvent être considérés équivalents.
      • 'accent' : Les accents comptent, la casse ne compte pas.
        Exemple : a et á ne sont pas équivalents, mais a et A peuvent l'être.
      • 'case' : La casse compte, les accents ne comptent pas.
        Exemple : a et A ne sont pas équivalents, mais a et á peuvent l'être.
      • 'variant' : Comparaison la plus stricte. Casse et accents comptent.
        Exemple : a, A et á sont traités comme différents.
    • numeric (facultatif, défaut : false)
      Permet un tri plus naturel quand des nombres sont intégrés dans du texte.
      • true : tri des chaînes numériques prises en compte.
        Exemple : '2' est placé avant '10' dans une liste de textes.
      • false : tri purement textuel.
        Exemple : '10' peut être placé avant '2'.
    • ignorePunctuation (facultatif, défaut : false) : Indique si la ponctuation doit influencer la comparaison. Si la ponctuation est ignorée, !, -, ., ,, etc. sont traités comme s'ils n'existaient pas dans le tri.
      • true
        Exemple : 'chat' et 'chat!' peuvent être considérés très proches, car ! est ignoré.
      • false
        Exemple : 'chat' et 'chat!' peuvent être ordonnés différemment, car ! est pris en compte.
    • caseFirst (facultatif, défaut : 'false')
      Décider si les majuscules doivent être privilégiées dans l'ordre quand la casse est pertinente.
      • 'upper' : Majuscules en premier.
        Exemple : 'A' peut être placé avant 'a'.
      • 'lower' : Minuscules en premier.
        Exemple : 'a' peut être placé avant 'A'.
      • 'false' : Ne pas forcer cet aspect.
        Exemple : l'ordre entre 'A' et 'a' dépendra surtout de sensitivity et de la locale.
    • usage (facultatif, défaut : 'sort')
      Choisir l'objectif de la comparaison.
      • 'sort' : Optimisé pour du tri.
        Exemple : trier une liste de produits à afficher.
      • 'search' : Optimisé pour de la recherche.
        Exemple : comparer un texte saisi avec une liste pour trouver des correspondances plus souples.

L'exemple suivant illustre une configuration typique pour trier des titres d'épisodes en français, en ignorant la casse et les accents, et en activant un tri "numérique" dans le texte.


                    const titres = ['Episode 2', 'épisode 10', 'Épisode 1', 'episode 12'];

                    const titresTries = titres.toSorted((a, b) =>
                    {
                        return a.localeCompare(b, 'fr', {
                            sensitivity: 'base',
                            numeric: true
                        });
                    });

                    console.log(titresTries);
                    // Affiche : [ 'Épisode 1', 'Episode 2', 'épisode 10', 'episode 12' ]
                

Inverser un tableau avec reverse() et toReversed()

Inverser un tableau consiste à mettre le dernier élément en premier, l'avant-dernier en deuxième, etc. Cette opération est souvent utilisée après un tri pour passer d'un ordre croissant à un ordre décroissant, ou simplement pour retourner l'ordre d'affichage d'une liste.

La différence entre reverse() et toReversed() est la même qu'entre sort() et toSorted()

  • reverse() modifie le tableau d'origine.
  • toReversed() renvoie une copie inversée sans toucher au tableau de départ.

                    const notes = [12, 18, 9, 15];

                    // Inverser sans modifier le tableau d'origine.
                    const notesInversees = notes.toReversed();

                    console.log('Original', notes); // Affiche : [12, 18, 9, 15]
                    console.log('Inversé', notesInversees); // Affiche : [15, 9, 18, 12]
                

Chaîner les méthodes

En JavaScript, il est courant de chaîner ces méthodes, c'est-à-dire d'en appeler plusieurs de manière consécutive à partir d'un tableau ou d'un objet de départ. Cette pratique consiste à appliquer une méthode sur la valeur retournée par la méthode précédente, que ce soit un tableau transformé, un objet modifié ou un résultat intermédiaire. Le chaînage permet ainsi de réaliser plusieurs opérations successives de manière claire et structurée.


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

                        // Créer un nouveau tableau contenant les nombres pairs à l'aide de la méthode prédéfinie "filter()".
                        // Ensuite, additionner toutes les valeurs provenant de ce nouveau tableau à l'aide de la méthode prédéfinie "reduce".
                        const resultat = nombres
                            .filter(nombre => nombre % 2 === 0)
                            .reduce((acc, nombre) => acc + nombre, 0);

                        /*
                            1. Appliquer la fonction prédéfinie "filter()" sur le tableau "nombres" : [1, 2, 3, 4, 5]

                            Valeur du filtre à chaque itération :

                                            nombre => nombre % 2 === 0   résultat : tableau de sortie
                            ----------------------------------------------------------------------------------
                            1ère itération :   1   =>    1   % 2 === 0      false : ignoré
                            2ème itération :   2   =>    2   % 2 === 0       true : ajouté
                            3ème itération :   3   =>    3   % 2 === 0      false : ignoré
                            4ème itération :   4   =>    4   % 2 === 0       true : ajouté
                            5ème itération :   5   =>    5   % 2 === 0      false : ignoré


                            2. Appliquer la fonction prédéfinie "reduce()" sur le nouveau tableau : [2, 4]

                            Valeur de l'accumulateur à chaque itération :

                                             acc = acc + nombre   résultat
                            ------------------------------------------------------------
                            1ère itération : acc =  0  +   2             2
                            2ème itération : acc =  2  +   4             6
                        */
                        console.log(resultat); // Affiche : 6
                    

Si vous désirez obtenir plus d'informations sur le sujet, rendez-vous sur MDN Web Docs.

Exercices - Part 4

Exo-fonctions-14

  • Objectif : Mettre en forme une liste de tâches pour l'affichage dans une interface de type todo-list en utilisant map().
  • Instructions :
    1. Créez un fichier nommé exo-fonctions-14.js.
    2. Déclarez un tableau taches contenant plusieurs chaînes de caractères représentant des tâches simples :
      
                                          const taches = [
                                              "Envoyer le mail au client",
                                              "Préparer le devis",
                                              "Mettre à jour le site"
                                          ];
                                      
    3. À l'aide de la méthode map(), créez un nouveau tableau nommé tachesFormatees dans lequel chaque tâche est transformée en une chaîne prête à l'affichage, en ajoutant systématiquement le préfixe "→ " devant chaque libellé.

      Après l'appel à map(), le tableau tachesFormatees devra contenir les valeurs suivantes :
      
                                          [
                                              "→ Envoyer le mail au client",
                                              "→ Préparer le devis",
                                              "→ Mettre à jour le site"
                                          ];
                                      
    4. Affichez dans la console le tableau d'origine (taches), puis le tableau de sortie (tachesFormatees).

Exo-fonctions-15

  • Objectif : Générer les noms complets d'utilisateurs à partir de données partielles en utilisant map().
  • Instructions :
    1. Créez un fichier nommé exo-fonctions-15.js.
    2. Déclarez un tableau utilisateurs contenant les objets suivants :
      
                                          const utilisateurs = [
                                              { prenom: "Nour", nom: "El Amrani" },
                                              { prenom: "Marc", nom: "Renard" },
                                              { prenom: "Sophie", nom: "Lambert" }
                                          ];
                                      
    3. À l'aide de map(), construisez un nouveau tableau nomsComplets dans lequel chaque entrée est une chaîne au format "Prénom Nom".

      Après l'appel à map(), le tableau nomsComplets devra contenir les valeurs suivantes :
      
                                          [
                                              "Nour El Amrani",
                                              "Marc Renard",
                                              "Sophie Lambert"
                                          ]
                                      
    4. Affichez dans la console le tableau d'origine (utilisateurs), puis le tableau de sortie (nomsComplets).

Exo-fonctions-16

  • Objectif : Préparer des données produits pour l'affichage dans une fiche de catalogue, en calculant le prix TTC à l'aide de map().
  • Instructions :
    1. Créez un fichier nommé exo-fonctions-16.js.
    2. Déclarez un tableau produits contenant les objets suivants :
      
                                      const produits = [
                                          { nom: "Clavier", prixHT: 40 },
                                          { nom: "Souris", prixHT: 25 },
                                          { nom: "Écran 24 pouces", prixHT: 199 }
                                      ];
                                      
    3. Déclarez une constante tauxTVA contenant le taux de TVA à appliquer :
      
                                          const tauxTVA = 0.21;
                                      
    4. À l'aide de map(), construisez un nouveau tableau produitsAffiches dans lequel chaque entrée est un objet contenant les propriétés suivantes :
      • nom : le nom du produit,
      • prixHT : le prix hors TVA,
      • prixTTC : le prix toutes taxes comprises, calculé avec la formule prixHT * (1 + tauxTVA),
      • libelle : une chaîne prête pour l'affichage stockée au format "NomProduit : XX.XX € TTC".
      Après l'appel à map(), le tableau produitsAffiches devra contenir les valeurs suivantes :
      
                                          [
                                              {
                                                  nom: "Clavier",
                                                  prixHT: 40,
                                                  prixTTC: 48.4,
                                                  libelle: "Clavier : 48.40 € TTC"
                                              },
                                              {
                                                  nom: "Souris",
                                                  prixHT: 25,
                                                  prixTTC: 30.25,
                                                  libelle: "Souris : 30.25 € TTC"
                                              },
                                              {
                                                  nom: "Écran 24 pouces",
                                                  prixHT: 199,
                                                  prixTTC: 240.79,
                                                  libelle: "Écran 24 pouces : 240.79 € TTC"
                                              }
                                          ];
                                      
    5. Affichez dans la console le tableau d'origine (produits), puis le tableau de sortie (produitsAffiches) pour vérifier que toutes les informations nécessaires sont présentes.

Exo-fonctions-17

  • Objectif : Préparer l'affichage des témoignages d'un site en ne conservant que les avis réellement positifs grâce à filter().
  • Instructions :
    1. Créez un fichier nommé exo-fonctions-17.js.
    2. Déclarez un tableau avis contenant les objets suivants :
      
                                          const avis = [
                                              { auteur: "Alice", note: 5 },
                                              { auteur: "Marc", note: 1 },
                                              { auteur: "Luc", note: 3 },
                                              { auteur: "Nour", note: 4 },
                                              { auteur: "Sophie", note: 2 }
                                          ];
                                      
    3. À l'aide de la méthode filter(), créez un nouveau tableau nommé avisPositifs contenant uniquement les avis dont la propriété note est supérieure ou égale à 3.
    4. Après l'appel à filter(), le tableau avisPositifs devra contenir les valeurs suivantes :
      
                                          [
                                              { auteur: "Alice", note: 5 },
                                              { auteur: "Luc", note: 3 },
                                              { auteur: "Nour", note: 4 }
                                          ];
                                      
    5. Affichez d'abord dans la console le nombre d'avis positifs retenus à l'aide de la propriété length du tableau avisPositifs, puis affichez le contenu complet de ce tableau.

Exo-fonctions-18

  • Objectif : Préparer la liste des personnes à contacter pour une newsletter en filtrant les abonnés encore actifs avec filter().
  • Instructions :
    1. Créez un fichier nommé exo-fonctions-18.js.
    2. Déclarez un tableau utilisateurs contenant les objets suivants :
      
                                          const utilisateurs = [
                                              { email: "alice@example.com", accepteNewsletter: true,  actif: true },
                                              { email: "marc@example.com", accepteNewsletter: false, actif: true },
                                              { email: "nour@example.com", accepteNewsletter: true,  actif: false },
                                              { email: "sophie@example.com", accepteNewsletter: true,  actif: true }
                                          ];
                                      
    3. À l'aide de filter(), créez un tableau destinataires contenant uniquement les utilisateurs qui ont accepté la newsletter (accepteNewsletter) et qui sont encore actifs (actif).
    4. Après l'appel à filter(), le tableau destinataires devra contenir les valeurs suivantes :
      
                                          [
                                              { email: "alice@example.com", accepteNewsletter: true, actif: true },
                                              { email: "sophie@example.com", accepteNewsletter: true, actif: true }
                                          ];
                                      
    5. Affichez dans la console la liste des emails présents dans destinataires.

Exo-fonctions-19

  • Objectif : Préparer les produits à afficher dans une catégorie du catalogue en filtrant par catégorie et par disponibilité avec filter().
  • Instructions :
    1. Créez un fichier nommé exo-fonctions-19.js.
    2. Déclarez un tableau produits contenant les objets suivants :
      
                                          const produits = [
                                              { nom: "Souris sans fil", categorie: "accessoires", stock: 12 },
                                              { nom: "Clavier mécanique", categorie: "accessoires", stock: 0  },
                                              { nom: "Écran 27 pouces", categorie: "ecrans", stock: 5  },
                                              { nom: "Tapis de souris XL", categorie: "accessoires", stock: 7  }
                                          ];
                                      
    3. À l'aide de filter(), créez un tableau produitsAffiches qui ne contient que
      • les produits appartenant à la catégorie accessoires (categorie),
      • ET dont le stock est strictement supérieur à zéro (stock).
    4. Après l'appel à filter(), le tableau produitsAffiches devra contenir les valeurs suivantes :
      
                                          [
                                              { nom: "Souris sans fil", categorie: "accessoires", stock: 12 },
                                              { nom: "Tapis de souris XL", categorie: "accessoires", stock: 7  }
                                          ];
                                      
    5. Affichez dans la console la liste finale des produits à afficher dans cette rubrique du site.

Exo-fonctions-20

  • Objectif : Calculer le chiffre d'affaires d'une journée à partir d'une liste de commandes grâce à reduce().
  • Instructions :
    1. Créez un fichier nommé exo-fonctions-20.js.
    2. Déclarez un tableau commandes contenant les montants suivants :
      
                                          const commandes = [120, 90, 45, 230];
                                      
    3. À l'aide de la méthode reduce(), calculez la somme de tous les montants du tableau pour obtenir le chiffre d'affaires total de la journée. Vous pouvez utiliser la formule :
      
                                          total = acc + montantActuel;
                                      
    4. Stockez ce résultat dans une constante nommée chiffreAffaires.
    5. Affichez dans la console un message au format :
      
                                          "Chiffre d'affaire du jour : X €"
                                      
      où X est la valeur de chiffreAffaires.

      Avec les données proposées, le message attendu est :
      
                                          "Chiffre d'affaire du jour : 485 €"
                                      

Exo-fonctions-21

  • Objectif : Construire un résumé statistique simple à partir des durées de connexion des utilisateurs avec reduce().
  • Instructions :
    1. Créez un fichier nommé exo-fonctions-21.js.
    2. Déclarez un tableau sessions contenant les objets suivants :
      
                                          const sessions = [
                                              { pseudo: "Alice", dureeMinutes: 30 },
                                              { pseudo: "Marc", dureeMinutes: 45 },
                                              { pseudo: "Nour", dureeMinutes: 25 },
                                              { pseudo: "Sophie",dureeMinutes: 50 }
                                          ];
                                      
    3. À l'aide de reduce(), calculez la durée totale passée sur le site et stockez ce résultat dans une constante dureeTotale. La formule de base sera :
      
                                          acc = acc + dureeActuelle;
                                      
    4. Calculez ensuite la durée moyenne par session en minutes, en divisant dureeTotale par le nombre de sessions présentes dans le tableau. Stockez ce résultat dans une constante dureeMoyenne.
    5. Affichez dans la console la durée totale et la durée moyenne par session :
      
                                          "Durée totale : X minutes"
                                          "Durée moyenne : Y minutes"
                                      
      où X est la valeur de dureeTotale, et Y est la valeur de dureeMoyenne.

      Avec les données proposées, le message attendu est :
      
                                          "Durée totale : 150 minutes"
                                          "Durée moyenne : 37.5 minutes"
                                      

Exo-fonctions-22

  • Objectif : Comptabiliser le nombre de tâches terminées pour préparer un résumé à afficher dans une interface de gestion de tâches.
  • Instructions :
    1. Créez un fichier nommé exo-fonctions-22.js.
    2. Déclarez un tableau taches contenant les objets suivants :
      
                                          const taches = [
                                              { intitule: "Envoyer le contrat", terminee: true  },
                                              { intitule: "Relancer le client", terminee: false },
                                              { intitule: "Mettre à jour le dossier", terminee: true  },
                                              { intitule: "Archiver les anciens mails", terminee: false }
                                          ];
                                      
    3. À l'aide de reduce(), comptez combien de tâches ont la propriété terminee égale à true. Vous pouvez utiliser une formule du type :
      
                                          acc = acc + 1
                                      
      uniquement lorsque la tâche est terminée.
    4. Stockez ce nombre dans une constante nommée nombreTachesTerminees.
    5. Affichez dans la console un message au format :
      
                                          "X tâches terminées sur Y"
                                      
      où X est la valeur de nombreTachesTerminees et Y est le nombre total de tâches dans le tableau. Avec les données proposées, le message attendu est :
      
                                          "2 tâches terminées sur 4"
                                      

Fonctions Récursives

Les fonctions récursives sont des fonctions qui s'appellent elles-mêmes pour résoudre un problème de manière itérative. Les fonctions récursives sont couramment utilisées pour résoudre des problèmes qui peuvent être divisés en sous-problèmes identiques ou similaires.

Une fonction récursive suit généralement cette structure de base :

  • Condition de base (cas de base) : Une déclaration conditionnelle qui vérifie si la fonction doit s'arrêter ou renvoyer une valeur directe. C'est la base de la récursivité pour éviter une boucle infinie.
  • Appel récursif : La fonction appelle elle-même avec des paramètres différents pour résoudre une partie plus petite du problème. Cette étape est importante pour converger vers le cas de base.

Nous allons créer une fonction récursive qui calculera la somme des entiers naturels de 1 à n. Nous utiliserons la récursion pour décomposer le calcul en sous-problèmes plus simples jusqu'à ce que la somme totale soit obtenue :


                    function calculerSommeArithmetique(n)
                    {
                        // Cas de base : si n est égal à 1, retourne simplement 1.
                        if (n === 1)
                        {
                            return 1;
                        }

                        // Appel récursif pour calculer la somme de 1 à N-1 et ajouter N.
                        return n + calculerSommeArithmetique(n - 1);
                    }

                    // Calcule la somme des nombres de 1 à 5.
                    const n = 5;
                    const somme = calculerSommeArithmetique(n);
                    console.log(`La somme arithmétique des nombres de 1 à ${n} est ${somme}.`); // Affiche : La somme arithmétique des nombres de 1 à 5 est 15.
                

Méthodes Personnalisées

On parle de méthode lorsqu'une fonction est déclarée au sein d'un objet ou d'une classe. Tout comme les fonctions, les méthodes peuvent être créées par l'utilisateur. Il suffit, pour cela, d'assigner une fonction anonyme à une propriété.


                    const personnage = {
                        pseudo: 'Lux',
                        saluer: function() {
                            // Récupérer la valeur de la propriété "pseudo".
                            // Utilisation du contexte "this" pour référencer l'objet lorsqu'une méthode accède à un autre élément de cet objet.
                            const pseudo = this.pseudo;
                            console.log(`${pseudo} vous dit bonjour!`);
                        }
                    }

                    // Appel de la méthode "saluer()" de l'objet "personnage".
                    personnage.saluer(); // Affiche : Lux vous dit bonjour!.
                

this est un mot-clé qui fait référence à l'objet qui appelle une méthode. Il permet à une fonction de savoir à quel objet elle appartient et d'accéder à ses propriétés.

La valeur de this peut varier en fonction du type de fonction employée.

  • Le this dans les fonctions fléchées dépend du contexte dans lequel la fonction a été définie.
  • Le this dans les fonctions classiques dépend de la manière dont la fonction est appelée.

Dans un objet littéral, il vaut mieux éviter d'utiliser des fonctions fléchées comme méthodes lorsque l'on a besoin de this. Les fonctions fléchées ne créent pas leur propre this et réutilisent celui du contexte extérieur. Dans un script de navigateur classique, ce contexte est souvent l'objet global window plutôt que l'objet lui-même.


                const personnage = {
                    pseudo: 'Lux',
                    saluer: () =>
                    {
                        // ATTENTION, À ÉVITER DANS CE CONTEXTE.
                        // Cette méthode est définie avec une fonction fléchée.
                        // Le mot-clé "this" ne fait pas référence à l'objet "personnage"
                        // mais au contexte extérieur (par exemple "window" dans un script de navigateur).
                        // La propriété "pseudo" ne sera donc pas trouvée sur "this".
                        const pseudo = this.pseudo;
                        console.log(`${pseudo} vous dit bonjour!`);
                    }
                };

                // Appel de la méthode "saluer()" de l'objet "personnage".
                personnage.saluer(); // Affiche : undefined vous dit bonjour!.
                

On privilégie donc les fonctions classiques pour les méthodes d'objets littéraux lorsque l'on utilise this. Plus tard, dans le chapitre consacré aux classes, nous verrons des situations où les fonctions fléchées seront utilisées à l'intérieur des méthodes ou comme propriétés de classe afin de conserver plus facilement la bonne valeur de this lorsque l'on passe une méthode en callback.

Documenter son Code avec JSDoc

JSDoc est un outil de génération de documentation pour JavaScript. Il permet aux développeurs de créer une documentation claire et structurée pour leur code, ce qui facilite la compréhension et la collaboration.

Bien que nous abordions ce sujet à ce stade, il est important de noter que JSDoc ne se limite pas à la documentation des fonctions. Ce type de documentation s'applique aussi aux variables, aux classes, aux méthodes, ...

Analyse de syntaxe

Voici à quoi ressemble la syntaxe de JSDoc


                        /**
                        * Calcule le double d'une valeur.
                        * @param {number} [b=10] - La valeur à doubler (par défaut 10 si non fournie).
                        * @returns {number} Le double de la valeur spécifiée.
                        * @example
                        * // Exemple d'utilisation :
                        * const resultat = calculerLeDouble(20);
                        * console.log(resultat); // Affiche 40
                        */
                        function calculerLeDouble(b = 10) 
                        {
                            // Retourner le résultat du double de la valeur passée en argument;
                            return (b * 2);
                        }
                    

Explications étape par étape :

  1. Nous utilisons /** pour commencer un commentaire JSDoc multiligne.
  2. Sur la ligne suivante, nous écrivons une description concise de ce que fait la fonction. Dans ce cas : "Calcule le double d'une valeur."
  3. Ensuite, nous utilisons @param pour définir les paramètres de la fonction. Dans ce cas, nous avons un paramètre b de type nombre (number). Le [b=10] signifie que b est un paramètre optionnel avec une valeur par défaut de 10 si aucun argument n'est fourni.
  4. Après avoir défini les paramètres, nous utilisons @returns pour spécifier ce que la fonction renvoie. Dans ce cas, la fonction renvoie un nombre (number).
  5. @example introduit une section d'exemple. Les exemples d'utilisation sont utiles pour montrer comment la fonction peut être utilisée dans un contexte réel.
  6. Enfin, nous utilisons */ pour terminer le commentaire JSDoc.

Les balises de JSDoc

Voici une description des balises JSDoc les plus couramment utilisées :

  • @param : Cette balise est utilisée pour documenter les paramètres d'une fonction ou d'une méthode. Elle indique le nom du paramètre, son type, et une description optionnelle.
  • @returns (ou @return) : Cette balise est utilisée pour documenter la valeur de retour d'une fonction ou d'une méthode. Elle spécifie le type de la valeur de retour et fournit une description optionnelle.
  • @type : Cette balise est utilisée pour documenter le type d'une variable, d'un paramètre ou d'un retour de fonction. Elle est souvent utilisée pour les variables de classe ou les propriétés d'objet.
    
                                    /**
                                    * Un objet représentant une personne.
                                    * @typedef {Object} Personne
                                    * @property {string} nom - Le nom de la personne.
                                    * @property {number} age - L'âge de la personne.
                                    */
                                
  • @description : Cette balise est utilisée pour fournir une description générale d'une fonction, d'une classe ou d'une autre entité. Elle est utile pour donner un aperçu rapide de ce que fait l'entité documentée.
    
                                /**
                                * Une classe représentant un livre.
                                * @class
                                * @description Cette classe représente un livre avec un titre et un auteur.
                                */
                                
  • @example : Cette balise est utilisée pour fournir des exemples d'utilisation d'une fonction, d'une méthode ou d'une classe. Les exemples montrent comment utiliser l'entité documentée dans des scénarios réels.

Nous n'entrerons pas en profondeur dans les fonctionnalités de JSDoc, mais notez qu'il existe un utilitaire en ligne de commande vous permettant de générer automatiquement une documentation lisible à partir de ces commentaires JSDoc. Si vous désirez en apprendre plus, rendez-vous sur la documentation officielle de JSDoc.