Une fonction permet d'éviter d'avoir à écrire plusieurs fois
le même code lorsqu'un même traitement doit être
mis en oeuvre à plusieurs endroits dans le programme.
Exemple 1.
Un programme dressant la table du 8 :
Si, dans le même programme, nous avons également besoin de la table de n = 9, de n = 7...
on pourrait copier-coller le code précédent. Mais il y a mieux à faire : nommer ce code en le transformant en fonction,
n devenant un paramètre de la fonction.
n est appelé paramètre de la fonction table.
Une fonction peut présenter zéro, ou un, ou plusieurs paramètres.
Exemple 2.
Si l'on veut pouvoir par exemple afficher la table de 1*n à 12*n (au lieu de l'affichage de 1*n à 10*n), on
peut ajouter un paramètre marquant le dernier coefficient voulu.
Image par une fonction
Les deux fonctions définies ci-dessus ne font pas correspondre d'image à n (ou au couple (n, fin) ).
Ces fonctions se contentent d'afficher à l'écran un message. Dans certains langages, on parle plutôt de procédure
que de fonction dans une telle situation.
A quoi ressemblerait une fonction faisant correspondre une image à l'entier n ?
Prenons l'exemple d'une fonction mathématique qui à un nombre x associe son carré :
On constate ci-dessus que le premier appel ( carre(3); ) ne donne aucun affichage.
C'est normal, l'appel consiste à appliquer la fonction au nombre 3 :
le calcul de 3*3 est effectué et la fonction se termine (on n'a pas demandé d'afficher le résultat).
Dans le second appel, le principe est le même : on calcule 5 * 5, mais cette fois ce calcul est suivi d'une demande d'affichage (avec document.write()). On obtient donc le résultat à l'écran.
Dans la dernière ligne, on voit que le résultat d'une fonction peut être utilisé comme le serait une valeur de variable.
Si l'on reprend l'exemple de la table de multiplication, on pourrait définir la fonction
table de façon à ce qu'elle "calcule" la chaîne de caractères correspondant à la table
mais sans affichage.
Le rôle de découpe en unités logiques
Un rôle important des fonctions est de découper votre programme en "petites unités logiques", en "petits modules
indépendants" faciles à comprendre.
L'usage des fonctions, par ce rôle, facilite la conception
d'un programme mais aussi sa relecture, sa réutilisation,
les corrections.
Nous verrons des exemples dans la suite (exercices...).
Présentons l'idée sur un exemple non détaillé.
Imaginons que nous programmions un jeu.
Dans ce jeu, une partie du programme gère l'affichage,
une partie gère l'enregistrement des scores, une partie gère l'évolution d'un personnage, etc...
Si tout le code (boucles, tests if ...) est mis bout à bout, même en étant commenté, il devient illisible.
Si par contre, chaque action importante est effectuée via une fonction portant un nom explicite (par exemple afficherScore()), alors le code devient plus facile à lire :
je ne lis qu'une seule fois le code nécessaire à l'affichage.
Lorsque je lis afficherScore(), je n'ai pas besoin de réfléchir :
je sais ce qui va être effectué.
Enfin si mon affichage n'est pas correct, je n'ai à modifier le code qu'à un seul endroit :
l'endroit où ma fonction d'affichage est définie et non tous les endroits où mon programme demande un affichage.
Si l'on multiplie cette remarque pour toutes les tâches,
on peut deviner le gain en lisibilité (et donc en maintenabilité) d'un code bien découpé en fonctions.
Le type d'une fonction
Le type d'une fonction, en langage javascript, est function.
La portée d'une variable
On peut dans un même programme avoir deux variables indépendantes et portant le même nom grâce à la notion de "portée".
Exemple 1
On constate que la valeur de n affichée en second est bien 7,
bien que la fonction ait été appelée entre temps.
La variable n définie dans la fonction f est une autre variable.
On peut imaginer que les deux variables sont indexées:
il y a la variable npartie principale du programme
et la variable nf.
On peut dans ce cas réécrire simplement l'ensemble du code sous une forme tout à fait équivalente :
Un intérêt essentiel de cette portée est de faciliter l'écriture de programmes sans bug.
Imaginez en effet que l'on ne dispose pas de cette notion de portée et que l'on soit contraint de nommer différemment toutes les variables :
Sur un code de quelques lignes comme ceux que l'on traite en exercice, cela n'est guère gênant.
Mais sur un code d'une véritable application de plusieurs milliers de lignes,
le nommage des variables deviendrait vite un vrai casse-tête.
Et si l'on devait reprendre un code pour l'améliorer quelques temps plus tard,
les erreurs seraient inévitables, on se retrouverait avec des variables nommées identiquement
alors qu'elles devraient logiquement être indépendantes. Cela provoquerait des bugs très pénibles à corriger.
Exemple 2
Au moment où l'on exécute le corps de la fonction,
le travail se fait avec une variable locale xf dans laquelle on a recopié la
valeur de xpartie principale.
La valeur de x de la partie principale est inchangée.
Exemple 3
Cette fois, il n'y a ni variable x déclarée dans le corps de la fonction, ni paramètre nommé x. De ce fait le x de la partie principale n'est pas masqué par les variables locales et ce ne peut être que lui qui est affecté dans la fonction.
Il faudra autant que possible éviter un usage tel que ce dernier usage. Si l'on veut affecter une variable x à l'aide d'une fonction, on préfèrera utiliser une valeur de retour de la fonction, comme suit :
Ce dernier code est tout à fait équivalent au suivant :