Commentaires

Les fonctions et paramètres


Les fonctions nommées


Arthur, l'apprenti développeurSalut ! Si j'ai bien compris aujourd'hui on va parler de fonctions !

Tout à fait Arthur, c'est un concept de développement très courant, qui permet de réutiliser autant qu'on le souhaite une suite d'instructions.

Arthur, l'apprenti développeurHum ok, tu pourrais être pus précis?

Et bien si on imagine un programme qui dit affiche l'heure, on pourrais faire ça :

{"language":"application/json","content":"var date = new Date();\nconsole.log(date);","filename":""}


Arthur, l'apprenti développeurOui on a déjà vu ça hier.

Mais si on doit afficher l'heure de nombreuses fois, il faudrait répéter ces deux lignes de code systématiquement. A la place on peut créer une fonction afficherHeure qui s'en occupera à notre place :

{"language":"application/json","content":"function afficherHeure(){\n\tvar date = new Date();\n\tconsole.log(date);\n}","filename":""}


Et il nous suffit ensuite d'appeler cette fonction autant de fois que nécessaire pour afficher l'heure :

{"language":"application/json","content":"function afficherHeure(){\n\tvar date = new Date();\n\tconsole.log(date);\n}\n\nafficherHeure();\nafficherHeure();\nafficherHeure();","filename":""}


Arthur, l'apprenti développeurAh je comprend, donc on peut prévoir un code à réutiliser et l'appeler à chaque fois qu'on en a besoin !

Exactement, l'exemple que je viens de faire s'appelle une fonction nommée en js, car on lui a donné un nom.

Arthur, l'apprenti développeurDonc il existe aussi des fonctions non nommés?

Les paramètres



Tout à fait, mais on y reviendra plus tard, avant ça je vais te parler de paramètres. Les fonctions sont pratiques, mais il faut aussi pouvoir les adapter. imaginons une fonction qui dit bonjour à quelqu'un, l'idéal serait qu'elle s'adapte au nom de la personne. Mais la fonction en elle même ne connais pas le nom de cette personne d'avance, on va donc lui passer ce nom en paramètres pour qu'elle puisse l'utiliser :

{"language":"application/json","content":"function direBonjour(prenom){\n\tconsole.log(\"Bonjour \" + prenom);\t\n}\n\ndireBonjour(\"Aurélien\"); // Affiche \"Bonjour Aurélien\"\ndireBonjour(\"Julien\"); // Affiche \"Bonjour Julien\"\ndireBonjour(\"Clémence\"); // Affiche \"Bonjour Clémence\"\ndireBonjour(\"Marie\"); // Affiche \"Bonjour Marie\"","filename":""}


Tu vois ici j'ai définit un paramètre prénom à ma fonction, c'est un peu comme une variable, mais qui n'existera que dans le contexte de cette fonction, on pourra ainsi lui passer une valeur différente à chaque fois.

Arthur, l'apprenti développeurAh ça c'est génial ! Mais on est obliger de définir une nouvelle chaine de caractère dans l'appel de fonction à chaque fois?

Non, je l'ai fait pour l'exemple, mais en général on va plutôt utiliser des variables pour rendre notre code plus générique. Par exemple on pourrait récupérer la liste des employés à partir d'une base de données, et les afficher grace à une fonction, pour l'exemple je définit ça dans un tableau, mais imagine que ce tableau soit récupéré d'ailleurs :

{"language":"application/json","content":"var listEmploye = [{\n\t\"nom\": \"Dupond\",\n\t\"prenom\": \"Jean\",\n\t\"age\": 31,\n\t\"poste\": {\n\t\t\"titre\": \"Ingénieur étude et développement\",\n\t\t\"dateEntree\" : \"17/10/2017\"\n\t}\n},{\n\t\"nom\": \"Dufresne\",\n\t\"prenom\": \"Marina\",\n\t\"age\": 24,\n\t\"poste\": {\n\t\t\"titre\": \"Chef de projet\",\n\t\t\"dateEntree\" : \"02/01/2019\"\n\t}\n}];\n\n\n\nfunction afficherEmploye(employe){\n\tconsole.log(\"Nom : \" + employe.nom);\n\tconsole.log(\"Prenom : \" + employe.prenom);\n\tconsole.log(\"Age : \" + employe.age);\n}\n\nfor(let employe of listEmploye){\n\tafficherEmploye(employe);\t\n}","filename":""}


Résultat :


Arthur, l'apprenti développeurAh ouais ! Mais par contre tu définit deux fois employe, une fois dans la boucle et une autre fois dans la fonction, ça ne va pas poser de problèmes?

Et bien non, c'est le gros avantage de la portée dont on a déjà parlé, le employe dans la boucle n'existe que dans la boucle, et celui dans la fonction n'existe que dans la fonction, donc ce sont bien deux variables différentes.

Arthur, l'apprenti développeurOk je comprend

Passage par valeur et par référence


Par contre il faut faire attention au passage par valeur et par référence.

Arthur, l'apprenti développeurPardon?

Quand on passe un paramètre à une fonction, de manière générale en informatique, on peut le faire par valeur et par référence. C'est une différence fondamentale.

Le passage par valeur va créer une copie de la variable pour qu'elle soit utilisée dans la fonction, alors que le passage par référence va passer un "lien" vers la variables pour qu'elle soit utilisée par la fonction. En javascript par défaut les variables sont passée par valeur, sauf si ce sont des objets et dans ce cas elles sont passées par référence.

Arthur, l'apprenti développeurEuh, ok et qu'est ce que ça change?

Et bien un passage par valeur est une copie, donc si on modifie la valeur du paramètre dans la fonction, ça ne changera que le paramètre et pas la variable qui a été passée. Par contre un passage par référence étant lié, si on modifie le paramètre dans la fonction, ça va aussi modifier la variable qui a été passée. Pour te montrer la différence voici un passage par valeur :

{"language":"application/json","content":"var x = 5;\n\nfunction double(x){\n x *= 2;\n\tconsole.log(x);// affiche 10\n}\n\ndouble(x);\nconsole.log(x);// affiche 5","filename":""}


Ici on voit bien que le paramètre x a été doublé avant d'être affiché, mais la variable x n'as pas été modifiée par la fonction. Par contre si on utilise un objet :

{"language":"application/json","content":"var x = {value : 5};\n\nfunction double(value){\n\tx.value *= 2;\n\tconsole.log(x);// affiche {value: 10}\n}\n\ndouble(x);\nconsole.log(x);// affiche {value: 10}","filename":""}


Cette fois ci notre variable a été modifiée par la fonction ! C'est un passage par référence.

Arthur, l'apprenti développeurAhhh mais pourquoi? Je commençais tout juste à comprendre que la variable x et le paramètres x étaient différents...

Parce qu'il est indispensable de le savoir, sinon à ton premier passage par référence, tu ne va pas comprendre pourquoi ton code modifie ton objet.

Il faut aussi savoir que l'on peut passer plusieurs paramètres à une fonction, il suffit de les séparer par une virgule :

{"language":"application/json","content":"function addition(a, b){\n\tconsole.log(a + b);\n}\n\naddition(5,3);// affiche 8","filename":""}


Arthur, l'apprenti développeurOk ça je comprend !

Retour de fonction


Et depuis tout à l'heure je ne fait que des console.log dans mes fonctions, mais il faut aussi savoir qu'une fonction peut retourner quelque chose, il suffit d'utiliser le mot clé return :

{"language":"application/json","content":"function addition(a, b){\n\treturn a + b;\t\n}\n\nvar total = addition(5,3);\nconsole.log(total);","filename":""}


Arthur, l'apprenti développeurAh ça c'est pratique !

Et on peut aussi passer autre chose que des variables ou des valeur à une fonction.

Arthur, l'apprenti développeurC'est à dire

Passage de fonction en paramètre



Et bien une fonction peut être passée comme paramètre à une autre fonction.

Arthur, l'apprenti développeurWhat? Une fonction à une fonction?

Oui, et il faut bien faire attention aussi, comme je t'ai parlé tout à l'heure du passage par valeur et par référence, on a un concept similaire pour les fonctions, on peut passer le résultat d'une fonction, ou la référence de la fonction.

Arthur, l'apprenti développeurTu m'as perdu...

Pour le passage de valeur c'est assez simple, on va appeler une fonction qui retourne une valeur, et utiliser cette valeur pour la passer à une autre fonction. Par exemple, si on a deux fonctions, une qui récupère la date et la formate, et une autre qui affiche un message "Nous sommes le {j/m/a}", on peut utiliser la première pour la passer à la deuxième en plusieurs lignes ça donnerais :

{"language":"application/json","content":"function dateFormatee(){\n\tvar date = new Date();\n\treturn date.toLocaleDateString();\n}\n\nfunction afficherDate(date){\n\tconsole.log(\"Nous somme le \" + date);\t\n}\n\nvar dateFormate = dateFormatee();\nafficherDate(dateFormate);","filename":""}


Mais passer par une variable est une ligne un peu inutile, on pourrais directement faire :

{"language":"application/json","content":"function dateFormatee(){\n\tvar date = new Date();\n\treturn date.toLocaleDateString();\n}\n\nfunction afficherDate(date){\n\tconsole.log(\"Nous somme le \" + date);\t\n}\n\nafficherDate(dateFormatee());","filename":""}


Et ça fonctionne tout aussi bien !

Arthur, l'apprenti développeurOk ! Et le passage par référence?

Je t'ai parlé de passage par référence pour comparer, mais quand on passe une fonction à une autre fonction, on appelle cela un callback. En fait la fonction appelée pourra elle même appeler le callback dans son exécution.

{"language":"application/json","content":"function faireUnTruc(callback){\n\tcallback();\t\n}\n\nfunction afficherDate(){\n\tvar date = new Date();\n\tconsole.log(date.toLocaleDateString());\n}\n\nfunction afficherHello(){\n\tconsole.log(\"Hello\");\n}\n\nfaireUnTruc(afficherDate);// affiche la date\nfaireUnTruc(afficherHello);// affiche \"Hello\"","filename":""}


Tu remarquera, à la différence de précédemment j'ai passer le nom de la fonction mais sans mettre les parenthèses après. C'est là qu'est la nuance, quand on met des parenthèse après un nom de fonction, c'est pour lui dire d'exécuter cette fonction, quand on ne les met pas, c'est pour faire référence à la fonction, mais sans l'éxecuter.

Arthur, l'apprenti développeurWhoa... Mais on ne doit pas s'en servir tous les jours de ça?

Les fonctions anonymes



Et bien, pour répondre à ta question de tout à l'heure, il existe des fonctions qui ne sont pas nommées, ce sont les fonctions anonymes. Et on va s'en servir très souvent pour créer un callback dans une fonction. Pour t'expliquer plus en détail pourquoi, il faut d'abord comprendre que le javascript est asynchrone, c'est à dire que lorsqu'une instruction est lancée, on ne va pas nécessairement attendre qu'elle soit terminée pour lancer l'instruction suivante. Bien entendu ce que l'on a fait jusque maintenant, l'affectation de variables, l'utilisation de structures conditionnelles et itératives est totalement synchrone, mais beaucoup d'autres instructions ne le sont pas.

Dans ces cas là, si l'on souhaite faire quelque chose en particulier lorsque cette instruction asynchrone est terminée, on ne peut pas le mettre juste après, sinon elle pourrait être exécutée avant la fin. Par exemple la fonction setTimeout va attendre un temps prédéfinit puis exécuter un code.

{"language":"application/json","content":"setTimeout(function(){\n\tconsole.log(\"Hello\");\n},1000) // attend 1000 millisecondes => 1 seconde\nconsole.log(\"World\");","filename":""}


Cet exemple va afficher "World" et seulement ensuite "Hello".

Arthur, l'apprenti développeurAh bon???

Et oui, en appelant le setTimeout on lui dit d'attendre 1 seconde avant d'afficher "Hello", puis on lui demande d'afficher "World", qu'il fait immédiatement, puis une seconde plus tard, il va afficher "Hello".

Et si tu regarde bien le code, setTimeout est une fonction à laquelle on a envoyé deux paramètres, le deuxième et le temps à attendre, et le premièr c'est une fonction anonyme, utilisé comme callback.

Arthur, l'apprenti développeurEt il y a beaucoup de fonctions asynchrone?

On aura l'occasion d'en voir plusieurs les prochains jours, entre autre avec la gestion des événements qui est totalement asynchrone ! Pour aujourd'hui c'est terminé.

Arthur, l'apprenti développeurJe suis impatient d'y être

Une chose à la fois, demain on verra le DOM déjà, on va arrêter les consoles.log et commencer à faire un vrai site en javascript !

Arthur, l'apprenti développeurJe suis encore plus impatient... Vivement demain ! J'ai terminé cette partie
Demander de l'assistance