Alain Barraud Mémento informatique  
 
Accueil Logithèque - SE Photo - vidéo Internet - protocoles Site Web PC - Réseau Archives
Page ouverte le 25/05/2009

JavaScript - Partie 8 : retour sur les fonctions

Retour sur les fonctions. C'est une notion récurrente en JS, et il y a encore pas mal de choses à en dire !

On sait déjà créer des fonctions qui prennent des arguments et qui renvoient des valeurs… bref, tout ce qu'il faut savoir pour les utiliser.

Ce chapitre va apporter un regard nouveau : on va découvrir qu'une fonction, ce n'est en fait… qu'une variable !
On va également apprendre à créer des fonctions auxquelles on peut donner n'importe quel nombre d'arguments, selon notre besoin…

Retour accueil JS


Sommaire partie 8

Fonctions et variables

Enregistrer une fonction dans… une variable ?!

Nous avons, dans les chapitres précédents, étudié séparément les variables et les fonctions.
Mais en fait, une fonction, ce n'est pas si différent que ça d'une variable…
Rappel
Une fonction se déclare dans l'en-tête de la page, de cette manière :
Code : JavaScript
1     function toto(arg1, arg2)
2     {
3     // code
4         return valeur;
5     }

Quelques points de rappel :
  • Le mot-clé function indique qu'on déclare une fonction
  • toto est le nom de la fonction
  • arg1 et arg2 sont les arguments de cette fonction, séparés par une virgule, et mis entre des parenthèses qui sont obligatoires (même s'il n'y a aucun argument).
  • On place le code de la fonction dans un bloc d'instructions, délimité par des accolades.
  • On renvoie éventuellement une valeur (c'est la valeur que "prendra" la fonction quand on l'appellera).
Créer une fonction dans une variable
Il existe une autre manière de créer une fonction, en utilisant une variable.
Comme cela :
Code : JavaScript
1     var toto = function(arg1, arg2)
2     {
3     // code
4     }

alt Cette fonction est exactement la même que celle au dessus (à l'exception du return valeur;).

Cette fois-ci :
  • On a déclaré notre variable, à l'aide du mot-clé var (comme vu dans le chapitre à ce sujet).
  • On a donné à notre variable le nom de toto.
  • On lui a affecté (c.a.d. on lui a donné pour valeur) une fonction, prenant deux paramètres nommés arg1 et arg2, et exécutant le code entre les accolades.
question Si ce sont les mêmes fonctions, quel est l'intérêt d'utiliser cette nouvelle méthode plutôt que l'ancienne ?
C'est en fait un autre point de vue, assez différent : en déclarant une fonction de cette manière, on voit clairement qu'on enregistre notre fonction dans une variable.

On peut donc très facilement :
  • créer une fonction locale ou globale
  • re-déclarer une fonction (autrement dit, modifier son code), simplement en modifiant la variable
  • créer des tableaux de fonctions
  • etc.
Exemple : un tableau de fonctions
Pour voir, créons un tableau de fonctions.
On commence par déclarer le tableau, comme vu auparavant, puis on associe à chaque élément (chaque case) une fonction, comme on vient de le voir :
Code : JavaScript
1     var operation = new Array();
2         operation["add"] = function(x,y) { return x+y; };
3         operation["soustr"] = function(x,y) { return x-y; };
4         operation["mult"] = function(x,y) { return x*y; };
5         operation["div"] = function(x,y) { return x/y; };
6         operation["moy"] = function(x,y) { return (x+y)/2; };

On fait un essai : on demande deux nombres à l'utilisateur, ainsi que le nom de la fonction à appliquer (add, soustr, mult, div ou moy).
Code : JavaScript
1     var a = parseFloat( prompt("Premier nombre ?") );
2     var b = parseFloat( prompt("Deuxieme nombre ?") );
3     var fct = prompt("Fonction a appliquer ?");
4
5     var resultat = operation[fct] (a,b);
6     alert("Resultat : "] + resultat);
C'est quand même mieux que d'effectuer cinq if à la suite

Rappel : portée des variables

En JavaScript, on distingue les variables globales (accessibles n'importe où dans son code) des variables locales.
Les variables locales déclarées dans une fonction ne seront accessibles (et "visibles") qu'à l'intérieur de cette fonction. On peut donc sans problème utiliser le même nom pour des variables locales de deux fonctions différentes.
alt Mieux vaut éviter d'utiliser des variables globales.

Comment ça marche ?
  • On déclare une variable locale à l'aide de var, dans le bloc d'instructions (qui est généralement "matérialisé" par des accolades) dans lequel elle doit être accessible.
  • Il y a deux façons de créer une variable globale : soit en la déclarant en dehors de tout bloc d'instructions (tout au début du code JS), soit on l'utilise sans la déclarer, comme si elle l'avait déjà été (on parle alors de déclaration implicite).

Un exemple :
Code : JavaScript
1     var a; // on declare une variable globale
2     function test(argument)
3     {
4         var resultat = 5*argument + 2;
5         a = argument; // modification de la variable globale
6         b = resultat; // declaration implicite d'une variable globale
7         return resultat;
8     }
  • Lorsque la fonction n'a pas été appelée, il y a une seule variable globale : a.
  • Quand on appelle la fonction, celle-ci modifie cette variable globale, et on en déclare (implicitement) une autre : b.
  • Une fois la fonction exécutée, on peut toujours accéder aux variables a et b, car elles sont globales.
  • Cependant, on ne peut accéder à la variable resultat uniquement à l'intérieur de la fonction, car c'est une variable locale.

Les arguments facultatifs : nombre fixé d'arguments

Voyons le fonctionnement

Comment ça se passe lorsqu'on appelle une fonction ?

Prenons une fonction :
Code : JavaScript
1     function f(x,y)
2     {
3     // code de la fonction
4     }
Elle a pour nom f, et prend deux paramètres, x et y.
question Que se passe-t-il si on l'appelle en ne précisant qu'un seul argument, comme ci-dessous ?
Code : JavaScript
1     f(5);

Essayons de comprendre ce qui va se passer lors de cet appel :
  • deux variables locales, x et y, vont être créées (ce sont les arguments)
  • la variable x va être initialisée avec la valeur 5 (c'est la valeur qu'on a donnée lors de l'appel pour le premier argument)
  • mais y ne sera pas initialisée, car on n'a pas précisé le second argument

Autrement dit, il va se passer quelque chose comme ceci :
Code : JavaScript
1     var x, y;
2     x = 5;
3     // code de la fonction

Une variable non initialisée !

Quels sont les symptômes ?
On se retrouve donc face à une variable déclarée, mais... qui n'a pas de valeur !
Si on pursuit, que va-t-il se passer avec un tel code ?
Code : JavaScript
1     var y;
2     alert(y);
On voit alors s'afficher : undefined.
alt En effet, undefined est un mot-clé signifiant que la variable est déclarée, mais qu'on ne lui a jamais donné de valeur.
Si la variable n'avait pas été déclarée, on n'aurait pas eu de message du tout : le script aurait été interrompu (l'ordinateur ne peut pas deviner qu'il s'agit d'une variable, puisqu'on ne l'a pas déclarée).
Comment ça se soigne ?
On peut effectuer un test comme celui-ci pour vérifier si la variable y est définie :
Code : JavaScript
1     if(y == underfined)

Et on peut aussi créer une fonction qui renvoie true ("vrai", cf. le chapitre sur les conditions) si la variable est définie, false sinon.
Code : JavaScript
1     function isDefined(variable)
2     {
3         return (variable != undefined);
4     }

alt Cette écriture peut perturber, cependant, c'est équivalent à ceci :
Code : JavaScript
1     function isDefined(variable)
2     {
3         if(variable == undefined)
4         return false;
5     else
6         return true;
7     }
Mais c'est "bête" d'écrire un tel code : "si condition est vrai, alors renvoie vrai, sinon renvoie faux "…

alt Noter que la valeur booléenne d'une variable non initialisée est false.
Ainsi, en effectuant le test if(y), si la variable y n'est pas initialisée, c'est comme si elle était initialisée avec false.

Exemple

Tout ça pour dire qu'on peut créer des fonctions pour lesquelles certains arguments sont facultatifs.

Un exemple : une fonction dist(a,b) qui calcule la distance entre a et b (autrement dit, la valeur absolue de b-a).
Mais si on appelle dist(a), on veut que la fonction calcule la valeur absolue de a (la distance entre a et 0).

Comment faire ?
Pour le calcul de la distance, pas de problème, on teste si a est plus grand que b, et on calcule a-b ou b-a selon le cas.
En revanche, la nouveauté se trouve dans le second point : si on ne donne qu'un seul paramètre, il va falloir initialiser b à 0.
Code : JavaScript
1     function dist(a,b)
2     {
3 // on initialise b a 0 si besoin
4         if(b == undefined)
5             b = 0;
6 // on continue ensuite normalement
7         if(a > b)
8             return a-b;
9         else
10             return b-a;
11     }

Au début de la fonction, il faut donc vérifier si les arguments facultatifs ont été précisés : si ce n'est pas le cas, il va falloir leur donner une valeur par défaut.

Les arguments facultatifs : nombre illimité d'arguments

question Tout ça ne permet pas de réaliser la fonction d'addition dont on a parlé quelques chapitres plus tôt, qui additionnait tous les arguments qu'elle avait !
On avait parlé d'une fonction qui devait pouvoir s'utiliser comme ceci :
Code : JavaScript
1     addition(12, 5); // qui donnerait 17
2     addition(21, 4, 15, 11, 6); // qui donnerait 57
3 // etc. avec autant de nombres qu'on veut

Oû sont stockés tous ces arguments ?

Ce qui serait pratique, c'est que les arguments soient numérotés… un peu comme... un tableau !
Justement, il est possible de récupérer un tableau qui contient tous les arguments de la fonction. Du coup, le problème semble tout de suite plus simple.
Un tableau contenant tous les arguments
Partons de la fonction d'addition qu'on veut réaliser. On peut déjà commencer par la créer comme ceci :
Code : JavaScript
1     function addition()
2     {
3     // corps de la fonction
4     };

Maintenant, il nous faut récupérer le tableau contenant les arguments.
Ce tableau est addition.arguments : ce sont les arguments de la fonction addition, stockés sous forme de tableau.

alt Ce tableau contient tous les arguments qu'on a donnés à la fonction lors de son appel.
Ainsi, en appelant addition(21, 4, 15, 11, 6), on aurait (schématiquement) :
Code : JavaScript
1     addition.arguments = new Array(21, 4, 15, 11, 6);
Petite parenthèse : le point rouge . ne rappelle-t-il pas monTableau.length, qui est la longueur de monTableau ?
Le tour est joué !
Commençons par créer une variable contenant ce tableau, qu'on va appeler nb (les arguments sont les nombres à additionner) :
Code : JavaScript
1     var nb = addition.arguments;
alt nb et addition.arguments sont un seul et même tableau !
Si on le modifie, il sera modifié sous les deux noms.

Et maintenant, il ne reste plus qu'à tout additionner (qui dit tableau, dit boucle…), ce qui nous donne cette fonction :
Code : JavaScript
1     function addition()
2     {
3         var nb = addition.arguments;
4         var somme = 0;
5         for(var i=0; i<nb.length; i++)
6             somme += nb[i];
7         return somme;
8     };

Ne reste plus qu'à tester :
Code : JavaScript
1     alert( addition(12, 5) );
2     alert( addition(21, 4, 15, 11, 6) );
Qui affiche bien 17, puis 57.

Noter qu'on peut très bien appeler addition(), sans aucun paramètre : le résultat est 0.
Un autre exemple
Pour que ce soit bien clair, étudions un nouvel exemple : une fonction, prenant encore une fois autant de paramètres (des nombres) qu'on veut, et qui renvoie le plus grand de ces nombres.

Cette fois-ci, à la différence de l'addition, on ne sait pas trop quoi renvoyer s'il n'y a aucun paramètre…
On va donc créer une fonction comme ceci :
Code : JavaScript
1     function maxi(m) { };

alt Dans le tableau d'arguments, la valeur de m sera également prise en compte, dans la première case (la case numéro 0).

On va ensuite parcourir le tableau, et enregistrer la plus grande valeur "lue" dans une variable (on peut utiliser la variable m) : cette variable contiendra donc le plus grand nombre parmi ceux qu'on a déjà parcourus.
Code : JavaScript
1     function maxi(m)
2     {
3         var nb = maxi.arguments; // on a donc m = nb[0]
4         for(var i=1; i<nb.length; i++)
5             if(nb[i] > m) // si l'argument est plus grand que le maximum…
6                 m = nb[i]; // … alors c'est le maximum
7         return m;
8     }

On peut tester :
Code : JavaScript
1     var n = maxi(7, 3);
2     alert(n);
3
4     var p = maxi(2, 8, 4, 1, 9, 4);
5     alert(p);
Remarque, cette dernière fonction peut s'avérer utile.