Le passage de paramètres
Prédire ce qu’une fonction modifie réellement chez son appelant.
Objectifs
À la fin de cette leçon, vous saurez :
- prédire si une fonction modifie les données de son appelant ;
- appliquer la règle « passage par valeur » aux primitives et aux références ;
- éviter les effets de bord involontaires dans vos fonctions.
🔗 Pour vous rafraîchir la mémoire : objets et références · découper avec des fonctions
La règle unique
JavaScript passe toujours les paramètres par valeur. Mais attention : pour un objet, la valeur transmise est… la référence. Tout s'explique par cette phrase.
primitive → la valeur est copiée → aucune répercussion extérieure
objet → la référence est copiée → mutations visibles à l'extérieur
Cas 1 : une primitive
function changer(x) {
x = 99;
}
let nombre = 42;
changer(nombre);
console.log(nombre); // 42 : inchangé
x est une copie locale de 42. L'affectation x = 99 ne modifie que cette copie. À la sortie de la fonction, nombre n'a jamais bougé.
Cas 2 : un objet
function renommer(user) {
user.name = "Jean";
}
const paul = { name: "Paul" };
renommer(paul);
console.log(paul.name); // "Jean" : changé !
Ici user est une copie de la référence : il pointe vers le même objet que paul. La mutation user.name = "Jean" touche cet objet commun.
Cas 3 : la nuance qui piège
function remplacer(user) {
user = { name: "Jean" }; // réaffectation locale
}
const paul = { name: "Paul" };
remplacer(paul);
console.log(paul.name); // "Paul" : inchangé !
user = { ... } ne mute rien : elle fait pointer la copie locale de la référence vers un nouvel objet. L'appelant ne voit jamais ce remplacement. Retenez le contraste :
| Dans la fonction | Effet chez l'appelant |
|---|---|
param.propriete = … (mutation) | visible |
param = nouvelleValeur (réaffectation) | invisible |
param est une primitive, param = … | invisible |
Le tableau aussi
function ajouterTache(liste) {
liste.push("nouvelle");
}
const taches = ["dormir"];
ajouterTache(taches);
console.log(taches); // ["dormir", "nouvelle"]
push est une mutation : l'appelant voit son tableau grossir. Si c'est voulu, parfait ; sinon, c'est un effet de bord.
Éviter les effets de bord
Deux styles pour une fonction qui « ajoute » :
// Style mutateur : modifie le tableau reçu
function ajouter(liste, element) {
liste.push(element);
}
// Style puriste : retourne un nouveau tableau, ne touche à rien
function ajouterSansEffet(liste, element) {
return [...liste, element];
}
Le second style rend le code plus prévisible — nous y reviendrons largement dans le chapitre sur les bonnes pratiques. Pour l'instant, la règle pratique : une fonction qui modifie ses paramètres doit être écrite et documentée comme telle.
Exercice
Pour chaque extrait, prédisez puis vérifiez :
function doubler(n) {
n = n * 2;
}
let valeur = 10;
doubler(valeur);
console.log(valeur);
function vider(liste) {
liste.length = 0;
}
const panier = ["pain", "lait"];
vider(panier);
console.log(panier);
function echanger(a, b) {
const temp = a;
a = b;
b = temp;
}
let x = 1, y = 2;
echanger(x, y);
console.log(x, y);
-
Écrivez
normaliserTitres(liste)qui reçoit un tableau de chaînes et renvoie un nouveau tableau en majuscules, sans modifier l'original. -
Expliquez pourquoi l'échange de la question 3 ne fonctionne pas, et comment faire fonctionner un échange avec des variables au niveau appelant.
Résumé
- Passage toujours par valeur ; pour un objet, la valeur est la référence.
- Mutations internes visibles dehors, réaffectations internes jamais.
- Une fonction doit soit muter explicitement, soit retourner du neuf — pas mélanger.
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
Réponses détaillées
Extrait 1. Affiche 10. n est une copie primitive ; la réaffectation interne est invisible.
Extrait 2. Affiche []. liste.length = 0 est une mutation du tableau pointé (on tronque son contenu). L'appelant voit son panier vidé.
Extrait 3. Affiche 1 2. Les paramètres a et b sont des copies locales ; leur échange interne ne remonte pas. Pour échanger réellement :
[x, y] = [y, x]; // déstructuration, au niveau appelant
ou retourner les nouvelles valeurs : return [b, a] puis affecter.
Question 4.
function normaliserTitres(liste) {
const resultat = [];
for (const titre of liste) {
resultat.push(titre.toUpperCase());
}
return resultat;
}
// vérification
const titres = ["intro", "conclusion"];
const majuscules = normaliserTitres(titres);
console.log(titres); // ["intro", "conclusion"] : intact
console.log(majuscules); // ["INTRO", "CONCLUSION"]
Question 5. Exemple de réponse attendue : « a et b sont des copies indépendantes des primitives ; échanger les copies ne change rien à l'extérieur. Un échange visible exige de travailler au niveau de l'appelant (déstructuration) ou de manipuler un objet conteneur dont on mute les propriétés ».