
call() et apply() exécutent immédiatement une fonction avec un this choisi, call reçoit les arguments un par un, apply les reçoit dans un tableau. bind() n’exécute rien : il renvoie une nouvelle fonction dont this (et éventuellement les premiers arguments) sont fixés pour toujours. On s’en sert quand une méthode est passée en callback et perd son objet.
Toutes les fonctions JavaScript possèdent trois méthodes, call(), apply() et bind(), qui servent la même cause : décider ce que vaut this au moment où la fonction s’exécute. Pour comprendre à quoi elles servent, il faut d’abord comprendre le problème qu’elles résolvent : en JavaScript, this ne dépend pas de l’endroit où une fonction est écrite, mais de la façon dont elle est appelée.
Le problème : this change selon l’appel
Quatre règles suffisent à prédire this dans presque tous les cas :
- Appel de méthode
objet.fonction():thisestobjet. - Appel simple
fonction():thisestundefineden mode strict (modules, classes), l’objet globalwindowen mode non strict. - Appel avec
new:thisest l’objet en cours de construction. - Appel via
call,applyou une fonction liée parbind:thisest ce que vous avez décidé.
Les fonctions fléchées échappent à ces règles : elles n’ont pas de this propre et gardent celui du code qui les entoure.
Le cas qui fait mal en pratique est le passage de la règle 1 à la règle 2 :
const person = {
name: 'Sarah',
hello() {
return `Bonjour ${this.name}`;
},
};
person.hello(); // 'Bonjour Sarah' : appel de méthode, this = person
const detached = person.hello;
detached(); // TypeError en mode strict : this vaut undefined
// ('Bonjour undefined' ou 'Bonjour ' en mode non strict)La fonction n’a pas changé. Seule la manière de l’appeler a changé, et this avec elle. C’est exactement ce qui se produit quand on passe une méthode à setTimeout, à addEventListener ou à map.
call() : appeler avec un this choisi
fonction.call(thisArg, arg1, arg2, …) exécute la fonction immédiatement, avec this égal à thisArg et les arguments passés un par un.
function presenter(age, ville) {
return `${this.name}, ${age} ans, ${ville}`;
}
const person = { name: 'Sarah' };
presenter.call(person, 30, 'Lille'); // 'Sarah, 30 ans, Lille'
presenter.call({ name: 'Léa' }, 25, 'Paris'); // 'Léa, 25 ans, Paris'
const detached = person.hello; // la méthode de l'exemple précédent
detached.call(person); // 'Bonjour Sarah' : this est rétabliUn usage classique : réutiliser une méthode de tableau sur quelque chose qui n’en est pas un.
Array.prototype.slice.call('abc'); // ['a', 'b', 'c']
// Aujourd'hui on écrit plutôt :
Array.from('abc'); // ['a', 'b', 'c']apply() : la même chose, arguments dans un tableau
fonction.apply(thisArg, [arg1, arg2, …]) fait exactement ce que fait call, mais reçoit les arguments sous forme de tableau. Pratique quand ils arrivent déjà groupés.
presenter.apply(person, [30, 'Lille']); // 'Sarah, 30 ans, Lille'
const notes = [3, 9, 4];
Math.max.apply(null, notes); // 9 : Math.max attend des arguments séparés, pas un tableauDepuis ES2015, l’opérateur de décomposition rend apply rarement nécessaire : Math.max(...notes) et presenter.call(person, ...args) font le même travail en restant lisibles. On rencontre surtout apply dans du code écrit avant.
bind() : fixer this pour plus tard
Là où call et apply exécutent tout de suite, bind n’exécute rien. Il renvoie une nouvelle fonction dont this est fixé définitivement. C’est l’outil des callbacks : on prépare la fonction maintenant, quelqu’un d’autre l’appellera plus tard.
const bound = person.hello.bind(person);
bound(); // 'Bonjour Sarah', quel que soit le contexte d'appel
bound.call({ name: 'X' }); // 'Bonjour Sarah' : bind gagne, call ne peut plus changer this
bound.name; // 'bound hello'Le cas typique : une méthode passée en callback
class Compteur {
constructor() {
this.n = 0;
}
incrementer() {
this.n += 1;
return this.n;
}
}
const compteur = new Compteur();
// Sans bind : la méthode est détachée de son objet
const inc = compteur.incrementer;
inc(); // TypeError: Cannot read properties of undefined (reading 'n')
// Avec bind : this est fixé à compteur
const incLie = compteur.incrementer.bind(compteur);
incLie(); // 1
incLie(); // 2Le même schéma s’applique aux minuteurs et aux événements du navigateur :
setTimeout(compteur.incrementer.bind(compteur), 1000);
bouton.addEventListener('click', compteur.incrementer.bind(compteur));
// Pour pouvoir retirer l'écouteur ensuite, gardez la référence liée :
const onClick = compteur.incrementer.bind(compteur);
bouton.addEventListener('click', onClick);
bouton.removeEventListener('click', onClick);Sans bind, addEventListener appellerait incrementer avec this égal au bouton cliqué : this.n serait undefined et le compteur ne compterait rien.
bind() pour l’application partielle
Les arguments passés à bind après this sont eux aussi fixés. La fonction renvoyée attend les suivants.
const presenterSarah = presenter.bind(person, 30);
presenterSarah('Lille'); // 'Sarah, 30 ans, Lille' : seul le dernier argument reste à fournir
const add = (a, b) => a + b;
const add5 = add.bind(null, 5); // this ne sert pas : null suffit
add5(3); // 8C’est une façon simple de spécialiser une fonction générique sans écrire une fonction enveloppe.
Fonctions fléchées : quand bind devient inutile
Une fonction fléchée capture le this de l’endroit où elle est écrite et l’ignore ensuite. Deux conséquences.
D’abord, call, apply et bind ne peuvent pas changer son this :
const arrow = () => this;
arrow.call({ a: 1 }); // this du module, pas { a: 1 }Ensuite, et c’est l’usage courant, une fléchée déclarée dans une méthode ou une classe conserve le bon this sans bind :
class Compteur {
n = 0;
// Propriété fléchée : this est capturé une fois pour toutes
incrementer = () => {
this.n += 1;
return this.n;
};
}
const c = new Compteur();
const inc = c.incrementer;
inc(); // 1 : fonctionne sans bind
setTimeout(() => c.incrementer(), 1000); // ou une fléchée à l'appelRevers de la médaille : une propriété fléchée est recréée pour chaque instance, alors qu’une méthode classique est partagée par le prototype. Pour quelques objets, aucune importance, pour des milliers d’instances, la méthode liée une fois dans le constructeur (this.incrementer = this.incrementer.bind(this)) économise de la mémoire.
this passé en second argument
Plusieurs méthodes de tableau acceptent directement la valeur de this à utiliser dans le callback, ce qui évite un bind :
const config = { k: 10 };
[1, 2, 3].map(function (x) { return this.k * x; }, config); // [10, 20, 30]forEach, filter, some et every ont le même second argument. Avec une fonction fléchée, il devient inutile.
Tableau comparatif
call | apply | bind | |
|---|---|---|---|
| Exécute la fonction | immédiatement | immédiatement | non : renvoie une fonction |
| Arguments | un par un | dans un tableau | un par un, fixés pour les appels futurs |
| Usage typique | emprunter une méthode | étaler un tableau (remplacé par ...) | callbacks, événements, application partielle |
| Effet sur une fonction fléchée | aucun sur this | aucun sur this | aucun sur this, les arguments sont fixés |
Erreurs fréquentes et comment les lire
- « Cannot read properties of undefined (reading ‘n’) » dans une méthode de classe : la méthode a été détachée de son instance (règle 2). Liez-la avec
bindou déclarez-la en propriété fléchée. - Un écouteur d’événement qui ne modifie pas l’état :
thisest l’élément DOM, pas votre objet. Même remède. - Un
bindqui semble ne rien faire : la fonction est une fléchée. Retirez lebind, il est inutile. removeEventListenerqui ne retire rien : chaquebindcrée une nouvelle fonction, il faut passer la même référence liée à l’ajout et au retrait.
Pour la suite, les classes en JavaScript reviennent sur les méthodes et le prototype, et le chapitre Les fonctions du tutoriel pose les bases si le vocabulaire de cet article est encore neuf. En PHP, la question se pose autrement : $this ne change jamais d’objet.
Erreurs fréquentes
bouton.addEventListener('click', compteur.incrementer) appelle la fonction avec this égal au bouton, pas à compteur. Écrivez compteur.incrementer.bind(compteur) ou une fonction fléchée.this propre : call, apply et bind ne le modifient pas. Seuls les arguments passés par bind sont pris en compte.bind crée une nouvelle fonction à chaque appel. Dans une boucle ou un rendu répété, liez une fois dans le constructeur (ou déclarez la méthode comme propriété fléchée).f.call(obj, [1, 2]) passe un seul argument, le tableau. Pour étaler un tableau, c’est apply, ou f.call(obj, ...tableau).

