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…
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 }
 |
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.
 |
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.
 |
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
.
 |
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
.
 |
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 }
 |
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 "…
|
 |
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
 |
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.
 |
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;
 |
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) { };
 |
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.