Séance 9 — Les fonctions

Écrire une fois, utiliser partout

  • Écrire une fonction avec des paramètres et un return
  • Comprendre la différence entre return et console.log
  • Supprimer les répétitions de son code

1. Le problème, en vrai

Souvenez-vous du jeu du nombre mystère (séance 6) : cinq blocs de code quasiment identiques. Et du compteur de cafés (séance 5) : les mêmes trois lignes recopiées dans deux boutons.

Ce n’est pas seulement moche. C’est dangereux : le jour où vous voulez changer le message, vous devez le modifier à cinq endroits. Vous en oublierez un.

ImportantLa règle d’or du programmeur

Ne jamais écrire deux fois la même chose.

Quand vous vous surprenez à copier-coller du code, arrêtez-vous : c’est le signal qu’il faut une fonction.

2. Une fonction, c’est une machine

Une fonction, c’est une machine :

   entrées                                    sortie
  (paramètres)  ──▶  ┌──────────────┐  ──▶  (return)
                     │   la machine │
      3, 4           │   a + b      │       7
                     └──────────────┘

On la construit une fois. On l’utilise autant qu’on veut.

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

console.log(additionner(3, 4));   // 7
console.log(additionner(10, 25)); // 35

La forme

function  additionner ( a , b ) {
└───┬───┘ └─────┬────┘ └──┬──┘
    │           │         └── les PARAMÈTRES : ce qui entre
    │           └──────────── le NOM de la fonction
    └──────────────────────── mot-clé obligatoire

  return a + b;    ← ce qui SORT de la machine
}
NoteDeux moments bien distincts
function direBonjour(prenom) {   // ① DÉFINITION : on construit la machine.
  return `Bonjour ${prenom} !`;  //    Rien ne s'exécute ici !
}

const message = direBonjour("Camille"); // ② APPEL : on s'en sert
console.log(message);                   // Bonjour Camille !

Écrire une fonction ne l’exécute pas. C’est comme écrire une recette de cuisine : tant que personne ne la suit, il n’y a pas de gâteau.

Étape 1 — Vos premières fonctions

app.js
function doubler(nombre) {
  return nombre * 2;
}

function estMajeur(age) {
  return age >= 18;
}

function saluer(prenom, ville) {
  return `Salut ${prenom}, comment ça va à ${ville} ?`;
}

console.log(doubler(21));                  // 42
console.log(estMajeur(15));                // false
console.log(saluer("Camille", "Palaiseau"));

Remarquez que estMajeur renvoie un booléen : une fonction peut renvoyer n’importe quel type.

3. return n’est pas console.log

C’est la confusion de la séance. Prenez le temps.

function doubler(n) {
  console.log(n * 2);
}

const resultat = doubler(5); // affiche 10
console.log(resultat);       // undefined 😱

La fonction affiche mais ne rend rien. Impossible de réutiliser le résultat.

function doubler(n) {
  return n * 2;
}

const resultat = doubler(5); // n'affiche rien
console.log(resultat);       // 10 ✅
console.log(doubler(5) + 1); // 11 ✅

La fonction rend une valeur. On peut la stocker, la recalculer, la réutiliser.

ImportantLa bonne image
  • console.log = parler à voix haute. Le son se perd.
  • return = rendre un résultat à celui qui a demandé. Il peut s’en servir.

Une fonction qui console.log sans return est une machine qui affiche le résultat sur un écran… mais ne le donne à personne.

Règle du cours : une fonction calcule et renvoie. C’est celui qui l’appelle qui affiche.

Avertissementreturn arrête tout de suite la fonction
function tester(n) {
  return n * 2;
  console.log("bonjour"); // ❌ jamais exécuté
}

Dès que JavaScript rencontre return, il sort de la fonction. Le code après est mort.

C’est aussi très utile :

function categorie(age) {
  if (age < 18) {
    return "mineur";     // on sort ici…
  }
  return "majeur";       // …donc on n'arrive ici que si age >= 18
}

Exercice 9.1

Écrivez trois fonctions :

  1. carre(n) qui renvoie n × n
  2. estPair(n) qui renvoie true si n est pair
  3. prixTTC(prixHT) qui renvoie le prix avec 20 % de TVA

Testez-les avec console.log.

app.js
function carre(n) {
  return n * n;
}

function estPair(n) {
  return n % 2 === 0;
}

function prixTTC(prixHT) {
  return prixHT * 1.2;
}

console.log(carre(7));     // 49
console.log(estPair(10));  // true
console.log(estPair(7));   // false
console.log(prixTTC(100)); // 120

Pour estPair, pas besoin de if ! n % 2 === 0 est déjà un booléen : on le renvoie directement. Écrire :

if (n % 2 === 0) { return true; } else { return false; }

fonctionne, mais c’est trois fois plus long pour le même résultat.

4. Les fonctions fléchées

Vous les utilisez depuis la séance 5 sans le savoir. Voici la même fonction, écrite des deux façons :

// Version « function »
function doubler(n) {
  return n * 2;
}

// Version fléchée — exactement la même chose
const doubler = (n) => {
  return n * 2;
};
const doubler = ( n )  =>  { return n * 2; };
      └──┬───┘   └┬┘   └┬┘   └──────┬─────┘
         │        │     │           └── le corps
         │        │     └────────────── la flèche
         │        └──────────────────── les paramètres
         └───────────────────────────── le nom (c'est une variable !)
NoteAlors pourquoi deux syntaxes ?

Pour des raisons historiques : function existe depuis 1995, la flèche depuis 2015. Aujourd’hui, les deux cohabitent et vous rencontrerez les deux.

Dans ce cours :

  • function quand on écrit une fonction qui a un nom et qu’on réutilise
  • flèche quand on passe une fonction à quelque chose d’autre — typiquement addEventListener

Vous comprenez maintenant cette ligne, écrite depuis la séance 5 :

bouton.addEventListener("click", () => {
  …
});

C’est une fonction sans nom et sans paramètre, qu’on donne à addEventListener pour qu’il l’exécute plus tard, au moment du clic. On l’appelle une fonction de rappel (callback).

5. Nettoyer le compteur de cafés

Étape 2 — Avant / après

Avant (séance 5) — les trois mêmes lignes deux fois :

ajouter.addEventListener("click", () => {
  cafes = cafes + 1;
  nombre.textContent = cafes;
  cout.textContent = `Coût total : ${(cafes * PRIX_CAFE).toFixed(2)} €`;
});

reset.addEventListener("click", () => {
  cafes = 0;
  nombre.textContent = cafes;
  cout.textContent = "Coût total : 0.00 €";
});

Après — on isole « mettre l’affichage à jour » dans une fonction :

cafe.js
const PRIX_CAFE = 1.1;
let cafes = 0;

function afficher() {
  nombre.textContent = cafes;
  cout.textContent = `Coût total : ${(cafes * PRIX_CAFE).toFixed(2)} €`;
}

ajouter.addEventListener("click", () => {
  cafes = cafes + 1;
  afficher();
});

reset.addEventListener("click", () => {
  cafes = 0;
  afficher();
});

afficher(); // affichage initial

Maintenant, si vous voulez changer le format du prix, il y a un seul endroit à modifier.

AstuceUne fonction sans paramètre et sans return, c’est permis ?

Oui. afficher() ne prend rien et ne rend rien : son travail est d’agir sur la page. On appelle ça un effet de bord, et c’est parfaitement normal pour une fonction d’affichage.

Notez les parenthèses vides obligatoires : afficher() l’appelle, afficher tout seul ne fait rien du tout (c’est juste le nom de la machine).

6. Attraper plusieurs éléments d’un coup

Pour enfin réparer le jeu du nombre mystère, il manque un outil :

const boutons = document.querySelectorAll(".choix");
Renvoie
querySelector le premier élément trouvé
querySelectorAll tous les éléments, dans une liste parcourable
for (const bouton of boutons) {
  console.log(bouton.textContent);
}

Étape 3 — Le nombre mystère, version courte

mystere.html
<button class="choix">1</button>
<button class="choix">2</button>
<button class="choix">3</button>
<button class="choix">4</button>
<button class="choix">5</button>

<p id="resultat">À toi de jouer.</p>

Tous les boutons partagent la même classe choix.

mystere.js
const resultat = document.querySelector("#resultat");
const boutons = document.querySelectorAll(".choix");

function tirerSecret() {
  return Math.floor(Math.random() * 5) + 1;
}

let secret = tirerSecret();

function jouer(choix) {
  if (choix === secret) {
    resultat.textContent = "Gagné ! 🎉";
    resultat.className = "gagne";
    secret = tirerSecret();
  } else {
    resultat.textContent = "Raté, essaie encore.";
    resultat.className = "perdu";
  }
}

for (const bouton of boutons) {
  bouton.addEventListener("click", () => {
    jouer(Number(bouton.textContent));
  });
}

Le même jeu, en trois fois moins de lignes. Et pour passer à 10 boutons, il suffit d’ajouter des <button class="choix"> dans le HTML : le JavaScript n’a pas besoin d’être touché.

AvertissementPourquoi Number(bouton.textContent) ?

textContent renvoie toujours du texte. Le bouton contient 3, mais on récupère "3".

Or "3" === 3 est faux (séance 6 !). Sans la conversion, on ne gagnerait jamais. C’est un bug très fréquent et difficile à voir : le code a l’air juste.

7. À toi de jouer

Exercice 9.2 — Prédire

Qu’affiche ce programme ?

function mystere(a, b) {
  if (a > b) {
    return a;
  }
  return b;
}

console.log(mystere(3, 8));
console.log(mystere(10, 2));
console.log(mystere(5, 5));

Et à quoi sert cette fonction ? Trouvez-lui un meilleur nom.

8
10
5

La fonction renvoie le plus grand des deux nombres. Un bon nom serait plusGrand(a, b) ou maximum(a, b).

Pour mystere(5, 5) : 5 > 5 est faux, donc on passe au return b, qui vaut 5.

Ce qu’il faut retenir : un nom de fonction doit dire ce qu’elle fait. Quand on lit maximum(x, y) dans un programme, on comprend sans aller voir le code.

Exercice 9.3 — Une fonction qui utilise une boucle

Écrivez moyenne(notes) qui prend un tableau de nombres et renvoie leur moyenne.

console.log(moyenne([10, 20]));       // 15
console.log(moyenne([12, 15, 8, 17])); // 13
app.js
function moyenne(notes) {
  let total = 0;

  for (const note of notes) {
    total = total + note;
  }

  return total / notes.length;
}

console.log(moyenne([10, 20]));        // 15
console.log(moyenne([12, 15, 8, 17])); // 13

Le return est après la boucle. S’il était à l’intérieur, la fonction sortirait dès le premier tour et renverrait un résultat faux.

Un paramètre peut donc être un tableau : c’est très courant.

Exercice 9.4 — Trouver les 3 erreurs

function saluer(prenom) {
  console.log(`Bonjour ${prenom}`);
}

const message = saluer["Camille"];
console.log(message.length);

function triple(n) {
  return;
  n * 3;
}
  1. saluer["Camille"] → on appelle une fonction avec des parenthèses, pas des crochets : saluer("Camille"). Les crochets servent aux tableaux.

  2. saluer ne renvoie rien (elle console.log). Donc message vaut undefined, et message.length provoque une erreur. Il faut un return :

    function saluer(prenom) {
      return `Bonjour ${prenom}`;
    }
  3. return; puis n * 3; sur la ligne suivante → return seul renvoie undefined, et la ligne d’après n’est jamais atteinte. La valeur doit être sur la même ligne que le return :

    function triple(n) {
      return n * 3;
    }

Exercice 9.5 — La calculatrice de pourboire 🧾

Faites une page pourboire.html avec :

  • Un affichage du montant de l’addition, au départ 50 €
  • Deux boutons +10 € et −10 € qui le modifient (jamais en dessous de 0)
  • Trois boutons 10 %, 15 %, 20 %
  • Un paragraphe qui affiche : Pourboire : X € — Total : Y €

Contraintes :

  • Une fonction calculerPourboire(montant, pourcentage) qui renvoie le pourboire
  • Une fonction afficher() qui met toute la page à jour
  • Aucune ligne de calcul dupliquée
pourboire.html
<!DOCTYPE html>
<html lang="fr">
  <head>
    <meta charset="UTF-8" />
    <title>Pourboire</title>
    <link rel="stylesheet" href="pourboire.css" />
  </head>
  <body>
    <h1>Calculatrice de pourboire</h1>

    <p>Addition : <span id="addition">50</span> €</p>
    <button id="plus">+10 €</button>
    <button id="moins">−10 €</button>

    <p>Pourboire souhaité :</p>
    <button class="taux" id="t10">10 %</button>
    <button class="taux" id="t15">15 %</button>
    <button class="taux" id="t20">20 %</button>

    <p id="resultat"></p>

    <script src="pourboire.js"></script>
  </body>
</html>
pourboire.js
const addition = document.querySelector("#addition");
const resultat = document.querySelector("#resultat");
const plus = document.querySelector("#plus");
const moins = document.querySelector("#moins");
const tauxBoutons = document.querySelectorAll(".taux");

let montant = 50;
let pourcentage = 10;

function calculerPourboire(montant, pourcentage) {
  return (montant * pourcentage) / 100;
}

function afficher() {
  const pourboire = calculerPourboire(montant, pourcentage);
  const total = montant + pourboire;

  addition.textContent = montant;
  resultat.textContent =
    `Pourboire (${pourcentage} %) : ${pourboire.toFixed(2)} € ` +
    `— Total : ${total.toFixed(2)} €`;
}

plus.addEventListener("click", () => {
  montant = montant + 10;
  afficher();
});

moins.addEventListener("click", () => {
  if (montant >= 10) {
    montant = montant - 10;
    afficher();
  }
});

for (const bouton of tauxBoutons) {
  bouton.addEventListener("click", () => {
    pourcentage = Number(bouton.textContent.replace(" %", ""));
    afficher();
  });
}

afficher();

Les points importants :

  • calculerPourboire fait un seul calcul et le renvoie. Elle ne touche pas à la page : on pourrait la réutiliser ailleurs.
  • afficher() est le seul endroit qui écrit dans la page. Appelée à la fin de chaque action, et une fois au démarrage.
  • La boucle sur les trois boutons de taux évite trois blocs copiés-collés.
  • .replace(" %", "") transforme "10 %" en "10" avant la conversion en nombre. (Une solution plus élégante existe avec les attributs data- : voir la fiche mémo JS.)

8. Fiche mémo

Définir

function nom(param1, param2) {
  return quelqueChose;
}

Appeler

const r = nom(valeur1, valeur2);

Parenthèses obligatoires, même vides : afficher()

Version fléchée

const nom = (param) => {
  return quelqueChose;
};

return ≠ console.log

return x;        // rend la valeur → réutilisable
console.log(x);  // l'affiche      → perdue

return arrête immédiatement la fonction.

Plusieurs éléments

const els = document.querySelectorAll(".classe");
for (const el of els) {
  el.addEventListener("click", () => { … });
}

Le piège

el.textContent est toujours du texte → Number(el.textContent)

9. Avant la prochaine séance