034.3 Leçon 2
Certification : |
Web Development Essentials |
---|---|
Version : |
1.0 |
Thème : |
034 Programmation JavaScript |
Objectif : |
034.3 Structures de contrôle et fonctions JavaScript |
Leçon : |
2 sur 2 |
Introduction
En plus de l’ensemble standard de fonctions intégrées fournies par le langage JavaScript, les développeurs peuvent écrire leurs propres fonctions personnalisées pour faire correspondre une entrée à une sortie adaptée aux besoins de l’application. Les fonctions personnalisées sont essentiellement un ensemble d’instructions encapsulées pour être utilisées ailleurs dans le cadre d’une expression.
L’utilisation de fonctions est un bon moyen d’éviter la duplication du code, car elles peuvent être appelées à partir de différents endroits du programme. De plus, le regroupement des instructions dans des fonctions facilite la liaison des actions personnalisées aux événements, ce qui est un aspect central de la programmation JavaScript.
Définir une fonction
À fur et à mesure qu’un programme évolue, il devient plus difficile d’organiser ce qu’il fait sans utiliser de fonctions. Chaque fonction possède sa propre portée de variable privée, de sorte que les variables définies dans une fonction ne seront disponibles qu’à l’intérieur de cette même fonction. Ainsi, elles ne seront pas mélangées avec les variables d’autres fonctions. Les variables globales sont toujours accessibles à l’intérieur des fonctions, mais la meilleure façon d’envoyer des valeurs d’entrée à une fonction est d’utiliser les paramètres de fonction. À titre d’exemple, nous allons nous appuyer sur le testeur de nombres premiers de la leçon précédente :
// A naive prime number tester
// The number we want to evaluate
let candidate = 231;
// Auxiliary variable
let is_prime = true;
// Start with the lowest prime number after 1
let factor = 2;
// Keeps evaluating while factor is less than the candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next number that will divide the candidate
factor++;
}
// Display the result in the console window
if ( is_prime )
{
console.log(candidate, "is prime");
}
else
{
console.log(candidate, "is not prime");
}
Si plus tard dans le code vous avez besoin de vérifier si un nombre est premier, il serait nécessaire de répéter le code déjà écrit. Cette pratique n’est pas recommandée, car toute correction ou amélioration du code original devrait être reproduite manuellement partout où le code a été copié. De plus, la répétition du code fait peser une charge sur le navigateur et le réseau, ce qui peut ralentir l’affichage de la page web. Au lieu de procéder ainsi, placez les instructions appropriées dans une fonction :
// A naive prime number tester function
function test_prime(candidate)
{
// Auxiliary variable
let is_prime = true;
// Start with the lowest prime number after 1
let factor = 2;
// Keeps evaluating while factor is less than the candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next number that will divide the candidate
factor++;
}
// Send the answer back
return is_prime;
}
La déclaration de fonction commence par une instruction function
, suivie du nom de la fonction et de ses paramètres. Le nom de la fonction doit suivre les mêmes règles que les noms des variables. Les paramètres de la fonction, également appelés arguments de la fonction, sont séparés par des virgules et encadrés par des parenthèses.
Tip
|
Il n’est pas obligatoire de lister les arguments dans la déclaration de la fonction. Les arguments passés à une fonction peuvent être récupérés dans un objet |
Dans l’exemple, la fonction test_prime
n’a qu’un seul argument : l’argument candidate
, qui est le nombre premier candidat à tester. Les arguments de fonction se comportent comme des variables, mais leurs valeurs sont attribuées par l’instruction qui appelle la fonction. Par exemple, l’instruction test_prime(231)
appellera la fonction test_prime
et assignera la valeur 231 à l’argument candidate
, qui sera alors disponible dans le corps de la fonction comme une variable ordinaire.
Si l’instruction appelante utilise des variables simples pour les paramètres de la fonction, leurs valeurs seront copiées dans les arguments de la fonction. Cette méthode — copier les valeurs des paramètres utilisés dans l’instruction d’appel vers les paramètres utilisés dans la fonction — est appelée passage des arguments par valeur. Toute modification apportée à l’argument par la fonction n’affecte pas la variable originale utilisée dans l’instruction appelante. Toutefois, si l’instruction appelante utilise des objets complexes comme arguments (c’est-à-dire un objet auquel sont attachées des propriétés et des méthodes) pour les paramètres de la fonction, ils seront passés comme référence et la fonction pourra modifier l’objet original utilisé dans l’instruction d’appel.
Les arguments qui sont passés par valeur, ainsi que les variables déclarées dans la fonction, ne sont pas visibles en dehors de celle-ci. En d’autres termes, leur portée est limitée au corps de la fonction où elles ont été déclarées. Néanmoins, les fonctions sont généralement utilisées pour créer des résultats visibles en dehors de la fonction. Pour partager une valeur avec sa fonction appelante, une fonction définit une instruction return
.
La fonction test_prime
de l’exemple précédent renvoie par exemple la valeur de la variable is_prime
. Par conséquent, la fonction peut remplacer la variable partout où elle serait utilisée dans l’exemple original :
// The number we want to evaluate
let candidate = 231;
// Display the result in the console window
if ( test_prime(candidate) )
{
console.log(candidate, "is prime");
}
else
{
console.log(candidate, "is not prime");
}
L’instruction return
, comme son nom l’indique, renvoie le contrôle à la fonction appelante. Par conséquent, où que soit placée l’instruction return
dans la fonction, rien de ce qui la suit n’est exécuté. Une fonction peut contenir plusieurs instructions return
. Cette pratique peut être utile si certaines d’entre elles se trouvent dans des blocs d’instructions conditionnelles, de sorte que la fonction peut ou non exécuter une instruction return
particulière à chaque exécution.
Certaines fonctions peuvent ne pas renvoyer de valeur, l’instruction return
n’est donc pas obligatoire. Les instructions internes de la fonction sont exécutées indépendamment de sa présence, de sorte que les fonctions peuvent également être utilisées, par exemple, pour modifier les valeurs des variables globales ou le contenu des objets passés par référence. Néanmoins, si la fonction n’a pas d’instruction return
, sa valeur de retour par défaut est définie comme undefined
: une variable réservée qui n’a pas de valeur et ne peut pas être modifiée.
Expressions de fonction
En JavaScript, les fonctions ne sont qu’un autre type d'objet. Ainsi, les fonctions peuvent être employées dans le script comme des variables. Cette caractéristique devient explicite lorsque la fonction est déclarée à l’aide d’une syntaxe alternative, appelée expressions de fonction :
let test_prime = function(candidate)
{
// Auxiliary variable
let is_prime = true;
// Start with the lowest prime number after 1
let factor = 2;
// Keeps evaluating while factor is less than the candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next number that will divide the candidate
factor++;
}
// Send the answer back
return is_prime;
}
La seule différence entre cet exemple et la déclaration de fonction de l’exemple précédent réside dans la première ligne : let test_prime = function(candidate)
au lieu de function test_prime(candidate)
. Dans une expression de fonction, le nom test_prime
est utilisé pour l’objet contenant la fonction et non pour nommer la fonction elle-même. Les fonctions définies dans les expressions de fonctions sont appelées de la même manière que les fonctions définies à l’aide de la syntaxe de déclaration. Toutefois, alors que les fonctions déclarées peuvent être appelées avant ou après leur déclaration, les expressions de fonction ne peuvent être appelées qu’après leur initialisation. Comme pour les variables, l’appel d’une fonction définie dans une expression avant son initialisation entraîne une erreur de référence.
Fonctions récursives
Outre l’exécution d’instructions et l’appel de fonctions intégrées, les fonctions personnalisées peuvent également appeler d’autres fonctions personnalisées, y compris elles-mêmes. L’appel d’une fonction à partir d’elle-même est appelé récursivité de la fonction. Selon le type de problème que vous essayez de résoudre, l’utilisation de fonctions récursives peut être plus simple que l’utilisation de boucles imbriquées pour effectuer des tâches répétitives.
Jusqu’à présent, nous savons comment utiliser une fonction pour vérifier si un nombre donné est premier. Supposons maintenant que vous vouliez trouver le prochain nombre premier suivant un nombre donné. Vous pourriez utiliser une boucle while
pour incrémenter le nombre candidat et écrire une boucle imbriquée qui cherchera des facteurs entiers pour ce candidat :
// This function returns the next prime number
// after the number given as its only argument
function next_prime(from)
{
// We are only interested in the positive primes,
// so we will consider the number 2 as the next
// prime after any number less than two.
if ( from < 2 )
{
return 2;
}
// The number 2 is the only even positive prime,
// so it will be easier to treat it separately.
if ( from == 2 )
{
return 3;
}
// Decrement "from" if it is an even number
if ( from % 2 == 0 )
{
from--;
}
// Start searching for primes greater then 3.
// The prime candidate is the next odd number
let candidate = from + 2;
// "true" keeps the loop going until a prime is found
while ( true )
{
// Auxiliary control variable
let is_prime = true;
// "candidate" is an odd number, so the loop will
// try only the odd factors, starting with 3
for ( let factor = 3; factor < candidate; factor = factor + 2 )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime.
// Test the next candidate
is_prime = false;
break;
}
}
// End loop and return candidate if it is prime
if ( is_prime )
{
return candidate;
}
// If prime not found yet, try the next odd number
candidate = candidate + 2;
}
}
let from = 1024;
console.log("The next prime after", from, "is", next_prime(from));
Notez que nous devons utiliser une condition constante pour la boucle while
(l’expression true
entre parenthèses) et la variable auxiliaire is_prime
pour savoir quand arrêter la boucle. Bien que cette solution soit correcte, l’utilisation de boucles imbriquées n’est pas aussi élégante que l’utilisation de la récursivité pour effectuer la même tâche :
// This function returns the next prime number
// after the number given as its only argument
function next_prime(from)
{
// We are only interested in the positive primes,
// so we will consider the number 2 as the next
// prime after any number less than two.
if ( from < 2 )
{
return 2;
}
// The number 2 is the only even positive prime,
// so it will be easier to treat it separately.
if ( from == 2 )
{
return 3;
}
// Decrement "from" if it is an even number
if ( from % 2 == 0 )
{
from--;
}
// Start searching for primes greater then 3.
// The prime candidate is the next odd number
let candidate = from + 2;
// "candidate" is an odd number, so the loop will
// try only the odd factors, starting with 3
for ( let factor = 3; factor < candidate; factor = factor + 2 )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime.
// Call the next_prime function recursively, this time
// using the failed candidate as the argument.
return next_prime(candidate);
}
}
// "candidate" is not divisible by any integer factor other
// than 1 and itself, therefore it is a prime number.
return candidate;
}
let from = 1024;
console.log("The next prime after", from, "is", next_prime(from));
Les deux versions de next_prime
retournent le nombre premier suivant le nombre donné comme seul argument (from
). La version récursive, comme la version précédente, commence par vérifier les cas particuliers (c’est-à-dire les nombres inférieurs ou égaux à deux). Ensuite, elle incrémente le candidat et commence à chercher tous les facteurs entiers avec la boucle for
(remarquez que la boucle while
n’est plus là). À ce stade, le seul nombre premier pair a déjà été testé, donc le candidat et ses facteurs possibles sont incrémentés de deux. (Un nombre impair plus deux est le prochain nombre impair).
Il n’y a que deux façons de sortir de la boucle for
dans l’exemple. Si tous les facteurs possibles sont testés et qu’aucun d’entre eux n’a un reste égal à zéro lors de la division du candidat, la boucle for
se termine et la fonction renvoie le candidat comme le prochain nombre premier après from
. Sinon, si facteur
est un facteur entier de candidat
(candidat % facteur == 0
), la valeur retournée provient de la fonction next_prime
appelée récursivement, cette fois avec le candidat
incrémenté comme paramètre from
. Les appels à next_prime
seront empilés les uns sur les autres, jusqu’à ce qu’un candidat ne trouve finalement aucun facteur entier. Ensuite, la dernière instance de next_prime
contenant le nombre premier le renverra à l’instance précédente de next_prime
, et ainsi successivement jusqu’à la première instance de next_prime
. Même si chaque appel de la fonction utilise les mêmes noms pour les variables, les appels sont isolés les uns des autres, de sorte que leurs variables sont conservées séparément en mémoire.
Exercices guidés
-
Quel type de surcharge les développeurs peuvent-ils atténuer en utilisant des fonctions ?
-
Quelle est la différence entre les arguments de fonction passés par valeur et les arguments de fonction passés par référence ?
-
Quelle valeur sera utilisée comme sortie d’une fonction personnalisée si elle n’a pas d’instruction de retour ?
Exercices d’exploration
-
Quelle est la cause probable d’un Uncaught Reference Error émis lors de l’appel d’une fonction déclarée avec la syntaxe expression ?
-
Ecrivez une fonction appelée
multiples_of
qui reçoit trois arguments :factor
,from
etto
. À l’intérieur de la fonction, utilisez l’instructionconsole.log()
pour afficher tous les multiples defactor
compris entrefrom
etto
.
Résumé
Cette leçon explique comment écrire des fonctions personnalisées dans le code JavaScript. Les fonctions personnalisées permettent au développeur de diviser l’application en “morceaux” de code réutilisable, ce qui facilite l’écriture et la maintenance de programmes plus importants. La leçon aborde les concepts et procédures suivants :
-
Comment définir une fonction personnalisée : déclarations de fonction et expressions de fonction.
-
Utilisation des paramètres comme entrée de la fonction.
-
Utilisation de l’instruction
return
pour définir la sortie de la fonction. -
Fonctions récursives.
Réponses aux exercices guidés
-
Quel type de surcharge les développeurs peuvent-ils atténuer en utilisant des fonctions ?
Les fonctions nous permettent de réutiliser le code, ce qui facilite sa maintenance. Un fichier de script plus petit permet également d’économiser de la mémoire et du temps de téléchargement.
-
Quelle est la différence entre les arguments de fonction passés par valeur et les arguments de fonction passés par référence ?
Lorsqu’il est passé par valeur, l’argument est copié dans la fonction et la fonction n’est pas en mesure de modifier la variable originale dans l’instruction appelante. Lorsqu’il est transmis par référence, la fonction est en mesure de manipuler la variable originale utilisée dans l’instruction appelante.
-
Quelle valeur sera utilisée comme sortie d’une fonction personnalisée si elle n’a pas d’instruction de retour ?
La valeur retournée sera définie comme
undefined
.
Réponses aux exercices d’exploration
-
Quelle est la cause probable d’un Uncaught Reference Error émis lors de l’appel d’une fonction déclarée avec la syntaxe expression ?
La fonction a été appelée avant sa déclaration dans le fichier de script.
-
Ecrivez une fonction appelée
multiples_of
qui reçoit trois arguments :factor
,from
etto
. À l’intérieur de la fonction, utilisez l’instructionconsole.log()
pour afficher tous les multiples defactor
compris entrefrom
etto
.function multiples_of(factor, from, to) { for ( let number = from; number <= to; number++ ) { if ( number % factor == 0 ) { console.log(factor, "*", number / factor, "=", number); } } }