Une classe qui s'appuie sur une autre
L'héritage en JavaScript permet à une classe de partir du modèle d'une autre classe et de l'étendre. Tu récupères gratuitement tous les champs et méthodes de la classe parente, et tu peux ajouter ou modifier ce que tu veux :
Dog extends Animal veut dire : « un Dog est un Animal, avec un petit truc en plus ». rex n'a pas de méthode speak à lui, mais la recherche retombe sur Animal, qui en possède une. Ce mécanisme de repli, c'est tout le principe de l'héritage en JavaScript — du chaînage de prototypes avec une syntaxe plus agréable.
super dans le constructeur
Une sous-classe qui définit son propre constructeur doit respecter une règle non négociable : appeler super(...) avant de toucher à this. super exécute le constructeur de la classe parente, et c'est justement lui qui crée et initialise réellement l'objet :
Si tu oublies la ligne super(name), tu te prends un ReferenceError dès que tu essaies de lire ou d'écrire sur this. Le moteur refuse catégoriquement de te donner accès à this tant que le parent n'est pas passé avant.
Quand une sous-classe ne déclare pas son propre constructeur, JavaScript en génère un automatiquement qui transmet tous les arguments à super — tu n'as donc besoin d'en écrire un que si tu ajoutes des champs ou que tu fais un peu de préparation en plus.
Redéfinir une méthode en JavaScript
Une sous-classe peut redéfinir n'importe quelle méthode dont elle hérite. C'est la version la plus proche dans la chaîne qui l'emporte :
Pas de magie ici : quand tu appelles speak() sur un Dog, le moteur cherche speak sur l'instance, puis sur Dog.prototype, le trouve, et s'arrête là. Il n'ira jamais jusqu'à Animal.prototype.
Étendre plutôt que remplacer : super.method()
Parfois, tu ne veux pas remplacer la méthode du parent — tu veux simplement y ajouter quelque chose. super.method(...) permet d'appeler la version du parent depuis la méthode redéfinie :
C'est précisément là que l'héritage en JavaScript prend tout son sens : la sous-classe réutilise la logique du parent au lieu de la recopier. Si Animal.describe évolue plus tard, Dog.describe en profite automatiquement.
super fonctionne dans n'importe quelle méthode, pas seulement dans le constructeur. Il fait toujours référence à la version de la classe parente de ce que tu appelles.
instanceof et la chaîne de prototypes
instanceof vérifie si la chaîne de prototypes d'un objet inclut une classe donnée. Toute instance d'une sous-classe est également une instance de ses classes parentes :
Les quatre renvoient true. La chaîne de prototypes suit l'ordre Puppy -> Dog -> Animal -> Object, et instanceof la parcourt de bout en bout. Pratique pour vérifier un type, même si en vrai vous y toucherez moins souvent que prévu — la plupart du temps, le code appelle juste des méthodes et laisse le polymorphisme faire le boulot.
Un exemple un peu plus poussé
Voici un schéma classique : une classe de base qui regroupe la logique commune, et quelques sous-classes qui viennent la spécialiser.
Remarque que describe vit sur Shape et qu'on n'a jamais besoin de la réécrire — elle se contente d'appeler this.area(), qui résout vers la bonne sous-classe à l'exécution. C'est ça, le polymorphisme : un même point d'appel, des comportements différents selon l'objet réel.
Héritage ou composition : que choisir ?
L'héritage est séduisant parce qu'il donne l'impression d'aller vite — une seule ligne et on hérite d'une tonne de méthodes. Mais il vieillit mal dès que les hiérarchies grossissent.
La règle en pratique : utilise extends quand la relation est clairement du type « X est un Y » et que la sous-classe partage vraiment l'essentiel du comportement du parent. Si tu tires sur l'héritage juste pour mutualiser une ou deux méthodes utilitaires, passe plutôt par la composition — donne à la classe un champ qui contient l'objet utilitaire :
Les arbres d'héritage profonds (Animal -> Mammal -> Dog -> WorkingDog -> PoliceDog) rendent super bien sur un diagramme, mais deviennent vite un cauchemar dans le code : une modification près de la racine se propage de façon imprévisible à travers tous les descendants. La plupart des bases de code saines restent à un ou deux niveaux de profondeur et s'appuient sur la composition pour le reste.
La suite : les membres statiques
Tout ce qui est présenté dans ce document vit sur les instances — des méthodes que l'on appelle via new Thing().something(). Parfois, on a besoin de méthodes ou de données qui appartiennent à la classe elle-même, et non à une instance en particulier. C'est exactement le rôle de static, et c'est ce qu'on va voir maintenant.
Questions fréquentes
Comment fonctionne l'héritage en JavaScript ?
Une classe peut hériter d'une autre via extends. La sous-classe récupère l'ensemble des méthodes et champs du parent, et peut en ajouter ou en redéfinir. En coulisses, JavaScript relie le prototype de la sous-classe à celui du parent : la recherche de méthode remonte donc automatiquement la chaîne.
À quoi sert super en JavaScript ?
super(...) appelle le constructeur de la classe parente — et il faut impérativement l'appeler avant d'utiliser this dans le constructeur d'une sous-classe. super.methode(...), lui, invoque la version parente d'une méthode : c'est la façon d'étendre un comportement au lieu de le remplacer complètement.
Faut-il choisir l'héritage ou la composition en JavaScript ?
L'héritage a du sens quand il y a une vraie relation « est un » et que la sous-classe partage l'essentiel du comportement du parent. Dès qu'il s'agit juste de réutiliser du code, mieux vaut passer par la composition — des objets qui en contiennent d'autres. Les hiérarchies de classes profondes vieillissent mal ; dans la vraie vie, on dépasse rarement un ou deux niveaux.