Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
JavaScript

Les prototypes : ce qui se cache derrière les classes

Les prototypes : ce qui se cache derrière les classes. Dans cette leçon du cours JavaScript, vous apprendrez à : comprendre que les classes JavaScript reposent sur les prototypes ; suivre la chaîne de...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

Les prototypes : ce qui se cache derrière les classes. Dans cette leçon du cours JavaScript, vous apprendrez à : comprendre que les classes JavaScript reposent sur les prototypes ; suivre la chaîne des prototypes pour trouver une propriété ; distinguer propriétés propres et propriétés héritées ; lire du code ancien écrit avec des fonctions constructeurs.

Chapitre 16 — La programmation orientée objet · 16.6 Les prototypes : ce qui se cache derrière les classes

🎯 Objectifs de la leçon
• comprendre que les classes JavaScript reposent sur les prototypes ;
• suivre la chaîne des prototypes pour trouver une propriété ;
• distinguer propriétés propres et propriétés héritées ;
• lire du code ancien écrit avec des fonctions constructeurs.

1. Une classe est une fonction

Nous avons vu que typeof CompteBancaire vaut "function". En JavaScript, les classes sont une syntaxe plus agréable (on parle de « sucre syntaxique ») pour un mécanisme plus ancien : les prototypes.

Chaque objet possède un lien caché, appelé [[Prototype]], vers un autre objet. Quand on lit une propriété absente de l'objet, JavaScript la cherche dans son prototype, puis dans le prototype du prototype, et ainsi de suite jusqu'à null.

Chaîne des prototypes d'une instance de ProduitPhysique
Figure 1 — La recherche d'une propriété remonte la chaîne.

2. Observer les prototypes

class CompteBancaire {
  constructor(titulaire, solde) {
    this.titulaire = titulaire;
    this.solde = solde;
  }
  deposer(montant) {
    this.solde += montant;
  }
}

const a = new CompteBancaire("Amina", 100);
const b = new CompteBancaire("Sami", 50);

// 1. Les données sont SUR l'objet, les méthodes sont sur le PROTOTYPE
console.log("Propriétés propres :", Object.keys(a));
console.log("a possède deposer ?", Object.hasOwn(a, "deposer"));
console.log("deposer est dans le prototype ?", Object.hasOwn(CompteBancaire.prototype, "deposer"));
console.log("Une seule fonction partagée ?", a.deposer === b.deposer);

// 2. La chaîne des prototypes
const p1 = Object.getPrototypeOf(a);
const p2 = Object.getPrototypeOf(p1);
console.log(p1 === CompteBancaire.prototype, p2 === Object.prototype, Object.getPrototypeOf(p2));

// 3. toString vient de Object.prototype, tout en haut de la chaîne
console.log("a.toString() :", a.toString());

// 4. Ajouter une méthode au prototype la donne à TOUTES les instances, même existantes
CompteBancaire.prototype.afficher = function () {
  return `${this.titulaire} : ${this.solde} DT`;
};
console.log(b.afficher());

// 5. Une propriété propre « masque » celle du prototype
a.afficher = () => "Je masque la méthode du prototype";
console.log(a.afficher(), "|", b.afficher());

Résultat dans la console

Propriétés propres : [ 'titulaire', 'solde' ]
a possède deposer ? false
deposer est dans le prototype ? true
Une seule fonction partagée ? true
true true null
a.toString() : [object Object]
Sami : 50 DT
Je masque la méthode du prototype | Sami : 50 DT
  • Les données (titulaire, solde) sont des propriétés propres de chaque objet.
  • Les méthodes sont rangées une seule fois dans Classe.prototype: toutes les instances les partagent.
  • toString vient de Object.prototype, en haut de presque toutes les chaînes.
  • Ajouter une méthode au prototype la rend disponible pour toutes les instances, même déjà créées.
  • Une propriété propre masque une propriété de même nom plus haut dans la chaîne.

3. Avant les classes

Avant ES2015, on écrivait des fonctions constructeurs. Vous en rencontrerez dans du code ancien et dans des bibliothèques :

// Ce qu'on écrivait AVANT les classes (ES5) : fonction constructeur + prototype
function CompteBancaire(titulaire, solde) {
  this.titulaire = titulaire;
  this.solde = solde;
}
CompteBancaire.prototype.deposer = function (montant) {
  this.solde += montant;
};

// Héritage « à la main »
function CompteEpargne(titulaire, solde, taux) {
  CompteBancaire.call(this, titulaire, solde); // équivalent de super(...)
  this.taux = taux;
}
Object.setPrototypeOf(CompteEpargne.prototype, CompteBancaire.prototype); // équivalent de extends
CompteEpargne.prototype.ajouterInterets = function () {
  this.deposer(this.solde * this.taux);
};

const e = new CompteEpargne("Lina", 1000, 0.05);
e.ajouterInterets();
console.log(e.solde, e instanceof CompteBancaire);

Résultat dans la console

1050 true
Avec classÉquivalent avec les prototypes
class A { constructor(x) { this.x = x; } }function A(x) { this.x = x; }
m() { … } dans la classeA.prototype.m = function () { … };
class B extends AObject.setPrototypeOf(B.prototype, A.prototype)
super(x)A.call(this, x)
⚠️ Attention
Modifier le prototype des objets intégrés (Array.prototype, String.prototype…) est à éviter absolument : votre méthode pourrait entrer en conflit avec une future version de JavaScript ou avec une bibliothèque.
ℹ️ À retenir
• Les classes reposent sur les prototypes : typeof Classe === "function".
• Chaque objet a un [[Prototype]]; une propriété absente est cherchée en remontant la chaîne jusqu'à null.
• Données = propriétés propres de l'objet ; méthodes = dans Classe.prototype, partagées.
• Object.getPrototypeOf, Object.hasOwn pour inspecter ; ne jamais modifier les prototypes intégrés.

🛠 Activité 6 — Explorer la chaîne des prototypes

Mission : Utilisez la console pour remonter les chaînes de prototypes d'objets que vous connaissez.

Questions pour avancer (répondez dans votre compte rendu, puis appliquez dans votre projet) :

  1. Pour un tableau [1, 2, 3], remontez la chaîne avec Object.getPrototypeOf jusqu'à null. Combien de maillons comptez-vous ? Où se trouve la méthode map? Et toString?
  2. Faites le même exercice avec un ProduitPhysique de la leçon 16.5. Dessinez la chaîne et indiquez dans quel maillon se trouve chaque méthode et chaque getter.
  3. Pourquoi Object.keys(livre) n'affiche-t-il pas prixTTC? Quelle méthode permet de savoir si une propriété est propre ou héritée ?
  4. Réécrivez votre classe Etudiant (activité 1) sous forme de fonction constructeur avec prototype. Le comportement est-il identique ? Qu'est-ce qui est plus pénible à écrire ?
  5. Créez un objet avec Object.create(null). Possède-t-il toString? À quoi un tel objet peut-il servir ?
  6. Un collègue ajoute Array.prototype.somme = function () { … } dans son projet. Donnez-lui deux raisons de ne pas le faire et proposez une alternative.

Je vérifie / je rends :

  • ☐ Le schéma de la chaîne de la question 2.
  • ☐ Les réponses aux autres questions.

← 16.5 L'héritage des classes (extends et super)16.7 Polymorphisme et composition →