Les fonctions et paramètres
Les fonctions nommées
Salut ! 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.
Hum 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":""}
Oui 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":""}
Ah 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.
Donc 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.
Ah ç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 :
Ah 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.
Ok 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.
Pardon?
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.
Euh, 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.
Ahhh 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":""}
Ok ç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":""}
Ah ça c'est pratique !
Et on peut aussi passer autre chose que des variables ou des valeur à une fonction.
C'est à dire
Passage de fonction en paramètre
Et bien une fonction peut être passée comme paramètre à une autre fonction.
What? 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.
Tu 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 !
Ok ! 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.
Whoa... 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".
Ah 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.
Et 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é.
Je 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 !
Je suis encore plus impatient... Vivement demain ! J'ai terminé cette partie