Héritage prototypal en JavaScript : hériter en déléguant, pas en copiant

L'héritage prototypal relie un objet à un autre pour lui emprunter son comportement. Aucun code n'est copié, tout est délégué à l'exécution.
3 min de lecture
Believemy logo

Dans la plupart des langages, hériter signifie recevoir une copie du plan de la classe parente. JavaScript fait autre chose : il relie un objet à un autre et lui laisse poser ses questions.

La différence n'est pas cosmétique. Elle explique pourquoi une méthode ajoutée après coup devient disponible sur des objets créés bien avant, et pourquoi class n'a jamais introduit de vrai système de classes.


Définition

L'héritage prototypal consiste à donner à un objet un Prototype, c'est-à-dire un autre objet auquel il délègue les propriétés qu'il ne possède pas. Rien n'est recopié : la recherche se fait à chaque lecture, en remontant la chaîne.

JAVASCRIPT
const animal = {
  presenter() {
    return "Je suis " + this.nom;
  },
};

const chien = Object.create(animal);
chien.nom = "Rex";

console.log(chien.presenter());                 // Je suis Rex
console.log(chien.hasOwnProperty("presenter")); // false
console.log(animal.isPrototypeOf(chien));       // true

chien ne possède que nom. La méthode vient d'animal, et this y désigne malgré tout chien, parce que sa valeur dépend de l'objet sur lequel l'appel est écrit.


Les classes écrivent exactement la même chose

class et extends sont une syntaxe posée par-dessus ce mécanisme. Le résultat est la même chaîne d'objets.

JAVASCRIPT
class Animal {
  constructor(nom) { this.nom = nom; }
  presenter() { return "Je suis " + this.nom; }
}

class Chien extends Animal {
  presenter() { return super.presenter() + ", un chien"; }
}

const rex = new Chien("Rex");
console.log(rex.presenter());                                // Je suis Rex, un chien
console.log(Object.getPrototypeOf(Chien.prototype) === Animal.prototype); // true
console.log(rex instanceof Animal);                          // true

Le super de la méthode redéfinie ne fait rien de magique : il appelle la version trouvée un cran plus haut dans la chaîne.


Trois pièges à connaître

  • Un objet partagé sur le prototype est partagé pour de bon. Un tableau placé là est le même pour toutes les instances, et une modification par l'une le change pour toutes.
  • Une propriété écrite ne remonte jamais. Affecter chien.presenter crée une propriété propre qui masque celle du prototype, sans toucher à l'original.
  • Les chaînes profondes coûtent en lisibilité. Trois niveaux d'héritage et il devient difficile de dire d'où vient une méthode. La composition est souvent préférable.
Bon à savoir

Pour partager du comportement sans construire une hiérarchie, Object.assign() copie des méthodes directement sur un objet. Le résultat n'est pas de l'héritage, mais il répond au même besoin dans bien des cas.


Questions fréquentes

Question

Faut-il préférer Object.create() ou class ?

Les deux produisent la même structure, donc le choix est une question de lisibilité et d'équipe. class est aujourd'hui la forme attendue dans la plupart des projets, et les outils la comprennent mieux. Object.create() reste précieux pour composer des objets sans constructeur, ou pour créer un objet sans prototype du tout.


Question

Peut-on hériter de plusieurs objets à la fois ?

Non, la chaîne est linéaire : un objet a exactement un prototype. Pour combiner plusieurs comportements, la pratique courante est le mixin, un objet de méthodes recopié sur le prototype cible avec Object.assign(). Ce n'est pas de l'héritage multiple, mais cela couvre le même besoin sans ambiguïté de résolution.


Question

Pourquoi mes instances partagent-elles un tableau que je croyais propre ?

Parce qu'il a été déclaré sur le prototype et non dans le constructeur. Une valeur primitive donne l'illusion de fonctionner, puisque l'écriture crée une propriété propre. Un tableau ou un Object (objet), lui, est lu puis modifié sur place, donc partagé. Initialisez toujours ces valeurs dans le constructeur.

Termes connexes

Découvrez notre glossaire JavaScript

Tous les mots de JavaScript expliqués simplement : mots-clés, objets natifs, méthodes, erreurs et concepts. Définitions claires et exemples qui tournent, pour apprendre et pour se dépanner.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.