28 avril 2023

Fonctions

TrĂšs souvent, nous devons effectuer une action similaire Ă  plusieurs endroits du script.

Par exemple, nous devons afficher un beau message lorsqu’un visiteur se connecte, se dĂ©connecte et peut-ĂȘtre ailleurs.

Les fonctions sont les principales “composantes” du programme. Ils permettent au code d’ĂȘtre appelĂ© plusieurs fois sans rĂ©pĂ©tition.

Nous avons déjà vu des exemples de fonctions intégrées, telles que alert(message), prompt(message, default) et confirm(question). Mais nous pouvons aussi créer nos propres fonctions.

Déclaration de fonction

Pour créer une fonction, nous pouvons utiliser une déclaration de fonction.

Cela ressemble Ă  ceci :

function showMessage() {
  alert( 'Hello everyone!' );
}

Le mot-clĂ© function commence en premier, puis le nom de la fonction, puis une liste de paramĂštres entre les parenthĂšses (sĂ©parĂ©s par des virgules, vides dans l’exemple ci-dessus, nous verrons des exemples plus tard) et enfin le code de la fonction, Ă©galement appelĂ© “le corps de la fonction”, entre des accolades.

function name(parameter1, parameter2, ... parameterN) {
 // body
}

Notre nouvelle fonction peut ĂȘtre appelĂ©e par son nom : showMessage().

Par exemple :

function showMessage() {
  alert( 'Hello everyone!' );
}

showMessage();
showMessage();

L’appel showMessage() exĂ©cute le code de la fonction. Ici, nous verrons le message deux fois, parce qu’on l’appelle deux fois.

Cet exemple illustre clairement l’un des principaux objectifs des fonctions: Ă©viter la duplication de code.

Si nous devons un jour modifier le message ou son affichage, il suffit de modifier le code Ă  un endroit: la fonction qui le renvoie.

Variables locales

Une variable dĂ©clarĂ©e Ă  l’intĂ©rieur d’une fonction n’est visible qu’à l’intĂ©rieur de cette fonction.

Par exemple :

function showMessage() {
  let message = "Hello, I'm JavaScript!"; // variable locale

  alert( message );
}

showMessage(); // Hello, I'm JavaScript!

alert( message ); // <-- Erreur! La variable est locale Ă  la fonction

Variables externes

Une fonction peut également accéder à une variable externe, par exemple :

let userName = 'John';

function showMessage() {
  let message = 'Hello, ' + userName;
  alert(message);
}

showMessage(); // Hello, John

La fonction a un accĂšs complet Ă  la variable externe. Cela peut aussi la modifier.

Par exemple :

let userName = 'John';

function showMessage() {
  userName = "Bob"; // (1) changé la variable externe

  let message = 'Hello, ' + userName;
  alert(message);
}

alert( userName ); // John avant l'appel de fonction

showMessage();

alert( userName ); // Bob, la valeur a été modifiée par la fonction

La variable externe n’est utilisĂ©e que s’il n’y a pas de variable locale.

Si une variable du mĂȘme nom est dĂ©clarĂ©e Ă  l’intĂ©rieur de la fonction, elle eclipsera la variable externe. Par exemple, dans le code ci-dessous, la fonction utilise le nom userName local. L’externe est ignorĂ© :

let userName = 'John';

function showMessage() {
  let userName = "Bob"; // déclarer une variable locale

  let message = 'Hello, ' + userName; // Bob
  alert(message);
}

// la fonction créera et utilisera son propre userName
showMessage();

alert( userName ); // John, inchangé, la fonction n'a pas accédé à la variable externe
Variables globales

Les variables déclarées en dehors de toute fonction, telle que userName externe dans le code ci-dessus, sont appelées globales.

Les variables globales sont visibles depuis n’importe quelle fonction (sauf si elles sont masquĂ©es par les variables locales).

C’est une bonne pratique de minimiser l’utilisation de variables globales. Le code moderne a peu ou pas de variable globales. La plupart des variables rĂ©sident dans leurs fonctions. Parfois, cependant, ils peuvent ĂȘtre utiles pour stocker des donnĂ©es au niveau du projet.

Arguments

Nous pouvons transmettre des donnĂ©es arbitraires Ă  des fonctions Ă  l’aide de paramĂštres.

Dans l’exemple ci-dessous, la fonction a deux paramùtres: from et text.

function showMessage(from, text) { // arguments : from, text
  alert(from + ': ' + text);
}

showMessage('Ann', 'Hello!'); // Ann: Hello! (*)
showMessage('Ann', "What's up?"); // Ann: What's up? (**)

Lorsque la fonction est appelée dans les lignes (*) et (**), les valeurs données sont copiées dans les variables locales from et text. Ensuite, la fonction les utilise.

Voici un autre exemple: nous avons une variable from et la transmettons Ă  la fonction. Remarque : la fonction change from, mais le changement n’est pas visible Ă  l’extĂ©rieur, car une fonction obtient toujours une copie de la valeur :

function showMessage(from, text) {

  from = '*' + from + '*'; // améliore l'apparence de "from"

  alert( from + ': ' + text );
}

let from = "Ann";

showMessage(from, "Hello"); // *Ann*: Hello

// la valeur de "from" est la mĂȘme, la fonction a modifiĂ© une copie locale
alert( from ); // Ann

Lorsqu’une valeur est passĂ©e en tant que paramĂštre de fonction, elle est Ă©galement appelĂ©e argument.

En d’autres termes, pour mettre ces termes au clair :

  • Un paramĂštre est la variable rĂ©pertoriĂ©e entre parenthĂšses dans la fonction dĂ©claration (c’est un terme du temps de la dĂ©claration).
  • Un argument est la valeur qui est transmise Ă  la fonction lorsqu’elle est appelĂ©e (c’est un terme du temps de l’appel).

Nous déclarons des fonctions en listant leurs paramÚtres, puis les appelons en passant des arguments.

Dans l’exemple ci-dessus, on pourrait dire : "la fonction showMessage est dĂ©clarĂ©e avec deux paramĂštres, puis appelĂ©e avec deux arguments : from et "Hello".

Les valeurs par défaut

Si une fonction est appelĂ©e, mais qu’aucun argument n’est fourni, alors la valeur correspondante devient undefined.

Par exemple, la fonction showMessage(from, text) mentionnĂ©e prĂ©cĂ©demment peut ĂȘtre appelĂ©e avec un seul argument :

showMessage("Ann");

Ce n’est pas une erreur. Un tel appel produirait "*Ann*: undefined". Comme la valeur de text n’est pas transmise, elle devient undefined.

Nous pouvons spĂ©cifier la valeur dite “par dĂ©faut” (Ă  utiliser si omise) pour un paramĂštre dans la dĂ©claration de fonction, en utilisant = :

function showMessage(from, text = "no text given") {
  alert( from + ": " + text );
}

showMessage("Ann"); // Ann: no text given

Maintenant, si le paramĂštre text n’est pas passĂ©, il obtiendra la valeur "no text given".

La valeur par dĂ©faut saute Ă©galement si le paramĂštre existe, mais est strictement Ă©gal Ă  undefined, comme ceci :

showMessage("Ann", undefined); // Ann: no text given

Ici, "no text given" est une chaĂźne de caractĂšres, mais il peut s’agir d’une expression plus complexe, qui n’est Ă©valuĂ©e et affectĂ©e que si le paramĂštre est manquant. Donc, cela est Ă©galement possible :

function showMessage(from, text = anotherFunction()) {
  // anotherFunction() est exécuté uniquement si aucun texte n'est fourni
  // son résultat devient la valeur de text
}
Évaluation des paramĂštres par dĂ©faut

En JavaScript, un paramÚtre par défaut est évalué chaque fois que la fonction est appelée sans le paramÚtre correspondant.

Dans l’exemple ci-dessus, anotherFunction() n’est pas du tout appelĂ©, si le paramĂštre text est fourni.

D’un autre cĂŽtĂ©, il est appelĂ© indĂ©pendamment Ă  chaque fois que text est manquant.

ParamĂštres par dĂ©faut dans l’ancien code JavaScript

Il y a plusieurs annĂ©es, JavaScript ne prenait pas en charge la syntaxe des paramĂštres par dĂ©faut. Les gens ont donc utilisĂ© d’autres moyens pour les spĂ©cifier.

De nos jours, on peut les croiser dans d’anciens scripts.

Par exemple, une vĂ©rification explicite pour undefined :

function showMessage(from, text) {
  if (text === undefined) {
    text = 'no text given';
  }

  alert( from + ": " + text );
}


Ou en utilisant l’opĂ©rateur || :

function showMessage(from, text) {
  // Si la valeur du texte est fausse, attribuez la valeur par défaut
  // cela suppose que text == "" est identique Ă  pas de texte du tout
  text = text || 'no text given';
  ...
}

ParamÚtres par défaut alternatifs

Il est parfois judicieux de définir des valeurs par défaut pour les paramÚtres non pas dans la fonction déclaration, mais à un stade ultérieur, lors de son exécution.

Nous pouvons vĂ©rifier si le paramĂštre est passĂ© lors de l’exĂ©cution de la fonction, en le comparant avec undefined :

function showMessage(text) {
  // ...

  if (text === undefined) { // si le paramĂštre est manquant
    text = 'empty message';
  }

  alert(text);
}

showMessage(); // empty message


Ou nous pourrions utiliser l’opĂ©rateur || :

function showMessage(text) {
  // if text is undefined or otherwise falsy, set it to 'empty'
  text = text || 'empty';
  ...
}

Les moteurs JavaScript modernes prennent en charge l’opĂ©rateur de coalescence des nuls ??, c’est mieux quand des valeurs fausses, telles que 0, sont considĂ©rĂ©es comme “normales” :

function showCount(count) {
  // if count is undefined or null, show "unknown"
  alert(count ?? "unknown");
}

showCount(0); // 0
showCount(null); // unknown
showCount(); // unknown

Renvoyer une valeur

Une fonction peut renvoyer une valeur dans le code appelant en tant que résultat.

L’exemple le plus simple serait une fonction qui additionne deux valeurs :

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

let result = sum(1, 2);
alert( result ); // 3

La directive return peut ĂȘtre n’importe oĂč dans la fonction. Lorsque l’exĂ©cution le permet, la fonction s’arrĂȘte et la valeur est renvoyĂ©e au code appelant (affectĂ© Ă  result ci-dessus).

Il peut y avoir plusieurs occurrences de return dans une seule fonction. Par exemple :

function checkAge(age) {
  if (age >= 18) {
    return true;
  } else {
    return confirm('Do you have permission from your parents?');
  }
}

let age = prompt('How old are you?', 18);

if ( checkAge(age) ) {
  alert( 'Access granted' );
} else {
  alert( 'Access denied' );
}

Il est possible d’utiliser return sans valeur. Cela entraĂźne la sortie immĂ©diate de la fonction.

Par exemple :

function showMovie(age) {
  if ( !checkAge(age) ) {
    return;
  }

  alert( "Showing you the movie" ); // (*)
  // ...
}

Dans le code ci-dessus, si checkAge(age) renvoie false, alors ShowMovie n’effectuera pas l’alert.

Une fonction avec un return vide ou rien dedans retourne undefined
function doNothing() { /* vide */ }

alert( doNothing() === undefined ); // true

Un return vide est également identique à un return undefined :

function doNothing() {
  return;
}

alert( doNothing() === undefined ); // true
N’ajoutez jamais de nouvelle ligne entre return et la valeur

Pour une longue expression dans return, il pourrait ĂȘtre tentant de la mettre sur une ligne sĂ©parĂ©e, comme ceci :

return
 (some + long + expression + or + whatever * f(a) + f(b))

Cela ne fonctionne pas, car JavaScript suppose un point-virgule aprĂšs le return. Cela fonctionnera comme :

return;
 (some + long + expression + or + whatever * f(a) + f(b))

Donc, cela devient effectivement un retour vide.

Si nous voulons que l’expression renvoyĂ©e recouvre plusieurs lignes, nous devons la dĂ©marrer Ă  la mĂȘme ligne que return. Ou du moins mettre les parenthĂšses d’ouverture comme suit :

return (
  some + long + expression
  + or +
  whatever * f(a) + f(b)
  )

Et cela fonctionnera comme prévu.

Nommer une fonction

Les fonctions sont des actions. Donc, leur nom est gĂ©nĂ©ralement un verbe. Il convient de dĂ©crire briĂšvement, mais aussi prĂ©cisĂ©ment que possible, le rĂŽle de la fonction. Pour qu’une personne qui lit le code reçoive le bon indice.

C’est une pratique rĂ©pandue de commencer une fonction avec un prĂ©fixe verbal qui dĂ©crit vaguement l’action. Il doit exister un accord au sein de l’équipe sur la signification des prĂ©fixes.

Par exemple, les fonctions qui commencent par "show" affichent généralement quelque chose.

Fonction commençant par


  • "get
" – retourne une valeur,
  • "calc
" – calcule quelque chose,
  • "create
" – crĂ©er quelque chose,
  • "check
" – vĂ©rifie quelque chose et retourne un boolĂ©en, etc.

Exemples de quelques noms :

showMessage(..)     // affiche un message
getAge(..)          // renvoie l'Ăąge (l'obtient en quelque sorte)
calcSum(..)         // calcule une somme et renvoie le résultat
createForm(..)      // crée un formulaire (et le retourne généralement)
checkPermission(..) // vérifie une permission, retourne vrai/faux

Avec les prĂ©fixes en place, un coup d’Ɠil sur un nom de fonction permet de comprendre le type de travail effectuĂ© et le type de valeur renvoyĂ©.

Une fonction – une action

Une fonction doit faire exactement ce qui est suggéré par son nom, pas plus.

Deux actions indĂ©pendantes mĂ©ritent gĂ©nĂ©ralement deux fonctions, mĂȘme si elles sont gĂ©nĂ©ralement appelĂ©es ensemble (dans ce cas, nous pouvons crĂ©er une troisiĂšme fonction qui appelle ces deux actions).

Quelques exemples de violation de cette rĂšgle :

  • getAge – serait mauvais si elle affichait une alert avec l’ñge (devrait seulement obtenir).
  • createForm – serait mauvais s’il modifiait le document en y ajoutant un formulaire (il ne devrait que le crĂ©er et le renvoyer).
  • checkPermission – serait mauvais s’il affiche le message d’accĂšs accordĂ©/refusĂ© (doit uniquement effectuer la vĂ©rification et renvoyer le rĂ©sultat).

Ces exemples supposent des significations communes de prĂ©fixes. Vous et votre Ă©quipe ĂȘtes libres de vous entendre sur d’autres sens, mais ils ne sont gĂ©nĂ©ralement pas trĂšs diffĂ©rents. Dans tous les cas, vous devez bien comprendre ce que signifie un prĂ©fixe, ce qu’une fonction prĂ©fixĂ©e peut et ne peut pas faire. Toutes les fonctions ayant le mĂȘme prĂ©fixe doivent obĂ©ir aux rĂšgles. Et l’équipe devrait partager ces connaissances.

Noms de fonction ultra-courts

Les fonctions utilisées trÚs souvent portent parfois des noms ultra-courts.

Par exemple le framework jQuery définit une fonction avec $. La librairie LoDash a nommé sa fonction principale _.

Ce sont des exceptions. En rĂšgle gĂ©nĂ©rale, les noms de fonctions doivent ĂȘtre concis et descriptifs.

Fonctions == Commentaires

Les fonctions doivent ĂȘtre courtes et faire exactement une seule chose. Si cette chose est consĂ©quente, il vaut peut-ĂȘtre la peine de scinder la fonction en quelques fonctions plus petites. Parfois, suivre cette rĂšgle peut ne pas ĂȘtre aussi facile, mais c’est dĂ©finitivement une bonne pratique.

Une fonction distincte est non seulement plus facile Ă  tester et Ă  dĂ©boguer – son existence mĂȘme est un excellent commentaire!

Par exemple, comparez les deux fonctions showPrimes(n) ci-dessous. Chacune extrait les nombres premiers jusqu’à n.

La premiĂšre variante utilise un label :

function showPrimes(n) {
  nextPrime: for (let i = 2; i < n; i++) {

    for (let j = 2; j < i; j++) {
      if (i % j == 0) continue nextPrime;
    }

    alert( i ); // un nombre premier
  }
}

La deuxiÚme variante utilise une fonction supplémentaire isPrime(n) pour tester la primalité :

function showPrimes(n) {

  for (let i = 2; i < n; i++) {
    if (!isPrime(i)) continue;

    alert(i);  // un nombre premier
  }
}

function isPrime(n) {
  for (let i = 2; i < n; i++) {
    if ( n % i == 0) return false;
  }
  return true;
}

La deuxiĂšme variante est plus facile Ă  comprendre, n’est-ce pas ? Au lieu du bloc de code, nous voyons le nom de l’action (isPrime). Parfois, les gens se rĂ©fĂšrent Ă  ce code comme Ă©tant auto-descriptif.

Des fonctions peuvent donc ĂȘtre créées mĂȘme si nous n’avons pas l’intention de les rĂ©utiliser. Ils structurent le code et le rendent lisible.

Résumé

Une déclaration de fonction ressemble à ceci :

function name(parameters, delimited, by, comma) {
  /* code */
}
  • Les valeurs transmises Ă  une fonction en tant que paramĂštres sont copiĂ©es dans ses variables locales.
  • Une fonction peut accĂ©der Ă  des variables externes. Mais cela ne fonctionne que de l’intĂ©rieur. Le code en dehors de la fonction ne voit pas ses variables locales.
  • Une fonction peut renvoyer une valeur. Si ce n’est pas le cas, le rĂ©sultat est undefined.

Pour rendre le code propre et facile Ă  comprendre, il est recommandĂ© d’utiliser principalement des variables et des paramĂštres locaux dans la fonction, et non des variables externes.

Il est toujours plus facile de comprendre une fonction qui possĂšde des paramĂštres, fonctionne avec eux et renvoie un rĂ©sultat, plutĂŽt qu’une fonction qui ne comporte aucun paramĂštre, mais modifie des variables externes comme un effet secondaire.

Nommage de fonction :

  • Un nom doit clairement dĂ©crire le rĂŽle de la fonction. Lorsque nous voyons un appel de fonction dans le code, un bon nom nous donne instantanĂ©ment une comprĂ©hension de ce qu’elle fait et de ce qu’elle retourne.
  • Une fonction est une action, les noms de fonctions sont donc gĂ©nĂ©ralement verbaux.
  • Il existe de nombreux prĂ©fixes de fonctions bien connus, tels que create
, show
, get
, check
 et ainsi de suite. Utilisez-les pour indiquer ce que fait une fonction.

Les fonctions sont les principaux Ă©lĂ©ments constitutifs des scripts. Maintenant que nous avons couvert les bases, nous pouvons donc commencer Ă  les crĂ©er et les utiliser. Mais ce n’est que le dĂ©but du chemin. Nous allons y revenir plusieurs fois, en approfondissant leurs fonctionnalitĂ©s avancĂ©es.

Exercices

importance: 4

La fonction suivante renvoie true si le paramÚtre age est supérieur à 18.

Sinon, il demande une confirmation et renvoie son résultat :

function checkAge(age) {
  if (age > 18) {
    return true;
  } else {
    // ...
    return confirm('Did parents allow you?');
  }
}

La fonction fonctionnera-t-elle différemment si else est supprimé ?

function checkAge(age) {
  if (age > 18) {
    return true;
  }
  // ...
  return confirm('Did parents allow you?');
}

Existe-t-il une différence dans le comportement de ces deux variantes ?

Aucune différence.

Dans les deux cas, return confirm('Did parents allow you?') s’exĂ©cute exactement lorsque la condition if est fausse.

importance: 4

La fonction suivante renvoie true si le paramÚtre age est supérieur à 18.

Sinon, il demande une confirmation et renvoie le résultat.

function checkAge(age) {
  if (age > 18) {
    return true;
  } else {
    return confirm('Did parents allow you?');
  }
}

Réécrivez-le, pour effectuer la mĂȘme chose, mais sans if, et en une seule ligne.

Faites deux variantes de checkAge :

  1. En utilisant un opĂ©rateur point d’interrogation ?
  2. En utilisant OU ||

En utilisant un opĂ©rateur point d’interrogation '?' :

function checkAge(age) {
  return (age > 18) ? true : confirm('Did parents allow you?');
}

En utilisant OU || (la variante la plus courte) :

function checkAge(age) {
  return (age > 18) || confirm('Did parents allow you?');
}

Notez que les parenthÚses autour de age > 18 ne sont pas obligatoires ici. Elles existent pour une meilleure lisibilité.

importance: 1

Ecrivez une fonction min(a, b) qui renvoie le plus petit des deux nombres a et b.

Par exemple :

min(2, 5) == 2
min(3, -1) == -1
min(1, 1) == 1

Une solution utilisant if :

function min(a, b) {
  if (a < b) {
    return a;
  } else {
    return b;
  }
}

Une solution utilisant l’opĂ©rateur point d’interrogation '?' :

function min(a, b) {
  return a < b ? a : b;
}

P.S. Dans le cas d’une Ă©galitĂ© a == b, peu importe ce qu’il faut retourner.

importance: 4

Ecrivez une fonction pow(x, n) qui renvoie x Ă  la puissance n. Ou, autrement dit, multiplie x par lui-mĂȘme n fois et renvoie le rĂ©sultat.

pow(3, 2) = 3 * 3 = 9
pow(3, 3) = 3 * 3 * 3 = 27
pow(1, 100) = 1 * 1 * ...* 1 = 1

Créez une page Web qui demande (prompt)x et n, puis affiche le résultat de pow(x, n).

Exécuter la démo

P.S. Dans cette tùche, la fonction ne doit prendre en charge que les valeurs naturelles de n : entiers supérieurs à 1.

function pow(x, n) {
  let result = x;

  for (let i = 1; i < n; i++) {
    result *= x;
  }

  return result;
}

let x = prompt("x?", '');
let n = prompt("n?", '');

if (n < 1) {
  alert(`Power ${n} is not supported, use a positive integer`);
} else {
  alert( pow(x, n) );
}
Carte du tutoriel

Commentaires

lire ceci avant de commenter

  • Si vous avez des amĂ©liorations Ă  suggĂ©rer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de prĂ©ciser.
  • Pour insĂ©rer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes – enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen
)