En JavaScript, une petite paire de parenthèses peut tout changer. Quand on écrit maFonction, on parle de la fonction elle-même. Quand on écrit maFonction(), on demande au moteur JavaScript de l’exécuter tout de suite. Cette différence paraît minime au début, mais elle revient partout : dans les événements, les callbacks, setTimeout, map, ou encore dans React.
C’est souvent là que les bugs commencent. On pense “je donne une fonction”, alors qu’en réalité on l’appelle immédiatement. Ou l’inverse : on pense qu’un morceau de code va s’exécuter, alors qu’on a juste passé une référence. Si vous avez déjà hésité devant un onclick, un addEventListener ou un then, c’est exactement le point à comprendre.
Contenu
Référencer une fonction, c’est passer la fonction elle-même
function direBonjour() {
console.log("Bonjour")
}
const action = direBonjour
const resultat = direBonjour()
Quand vous écrivez seulement le nom d’une fonction, sans parenthèses, vous passez une référence vers cette fonction.
function direBonjour() {
console.log("Bonjour");
}
const action = direBonjour;
Ici, action ne contient pas le résultat de direBonjour. Elle contient la fonction. Autrement dit, on stocke “quoi exécuter plus tard”. La fonction n’a pas encore tourné.
C’est très utile quand une autre partie du code doit décider du moment où elle appellera cette fonction. Par exemple, un bouton peut attendre un clic, ou un minuteur peut attendre quelques secondes avant de déclencher l’action.
On retrouve cette logique dans beaucoup d’exemples plus larges autour des fonctions JavaScript, notamment quand on manipule des callbacks ou des fonctions anonymes. Si vous voulez creuser cette famille de notions, jetez un œil à cet article sur les fonctions en JavaScript.
Appeler une fonction, c’est exécuter son code immédiatement
Dès que vous ajoutez des parenthèses, vous appelez la fonction.
function direBonjour() {
console.log("Bonjour");
}
direBonjour();
Là, JavaScript entre dans la fonction et exécute ce qu’elle contient. Si elle renvoie une valeur avec return, cette valeur remplace l’appel.
function addition(a, b) {
return a + b;
}
const resultat = addition(2, 3);
Dans cet exemple, resultat vaut 5. Ce n’est pas la fonction qui est stockée, c’est le résultat de son exécution.
C’est vraiment la distinction la plus simple à garder en tête :
maFonction: la fonctionmaFonction(): le résultat de la fonction
Pourquoi cette différence pose autant de problèmes en pratique ?
button.addEventListener("click", direBonjour)
setTimeout(direBonjour, 1000)
Parce que beaucoup d’API JavaScript attendent une fonction à appeler plus tard, pas un résultat calculé tout de suite.
Prenons un cas classique :
button.addEventListener("click", direBonjour);
Ici, c’est correct. On donne à addEventListener une fonction à lancer au moment du clic.
Maintenant, regardez cette version :
button.addEventListener("click", direBonjour());
Cette fois, direBonjour() s’exécute immédiatement, au moment où la ligne est lue. Le plus souvent, cela affiche déjà “Bonjour”, puis passe à addEventListener non pas une fonction, mais la valeur renvoyée par direBonjour. Et si cette valeur est undefined, l’événement ne fonctionnera pas comme prévu.
Le piège est le même avec setTimeout :
setTimeout(direBonjour, 1000); // bon
setTimeout(direBonjour(), 1000); // mauvais dans ce cas
Dans la première ligne, la fonction sera appelée dans une seconde. Dans la seconde, elle part tout de suite.
Que faire si vous devez passer des arguments ?
C’est souvent là qu’on comprend pourquoi la nuance existe. Si vous avez besoin d’arguments, vous ne pouvez pas écrire directement la référence simple direBonjour si la fonction doit être appelée avec une valeur précise plus tard.
function saluer(prenom) {
console.log(`Bonjour ${prenom}`);
}
Si vous écrivez :
button.addEventListener("click", saluer("Yvon"));
vous exécutez saluer tout de suite. Pour retarder l’appel, il faut envelopper l’exécution dans une autre fonction :
button.addEventListener("click", function () {
saluer("Yvon");
});
ou, plus souvent aujourd’hui :
button.addEventListener("click", () => saluer("Yvon"));
Cette fonction fléchée sert ici d’intermédiaire. On passe bien une fonction au gestionnaire d’événement, et cette fonction appellera saluer("Yvon") au bon moment.
Une image mentale simple pour ne plus se tromper
Vous pouvez voir une fonction référencée comme une télécommande posée sur la table. Elle est disponible, mais rien ne se passe tant que personne n’appuie sur le bouton. Une fonction appelée, c’est le moment où on appuie réellement dessus.
Cette image marche bien avec les tableaux aussi :
const nombres = [1, 2, 3];
const doubles = nombres.map(x => x * 2);
map attend une fonction. Il va l’utiliser pour chaque élément. Si vous débutez avec ce style de code, cela rejoint la différence entre déclaration, passage et exécution d’une fonction, un sujet proche de cet autre article consacré au JavaScript.
Retenez surtout ceci : sans parenthèses, vous transmettez un outil ; avec parenthèses, vous l’utilisez. Dès que vous repérez une API qui dit “donnez-moi une fonction”, la bonne question à se poser est simple : est-ce que je veux exécuter cette fonction maintenant, ou est-ce que je veux laisser quelqu’un d’autre l’exécuter plus tard ?
Dans la vraie vie, c’est presque toujours cette question qui fait la différence entre un code qui réagit au bon moment et un code qui part trop tôt.