Get to know MDN better
Cette page a été traduite à partir de l'anglais par la communauté. Vous pouvez contribuer en rejoignant la communauté francophone sur MDN Web Docs.
JavaScript est un langage fondé sur les prototypes : les comportements d'un objet sont déterminés par ses propres propriétés et par celles de son prototype. Cependant, avec l'ajout des classes, la création de hiérarchies d'objets et l'héritage de propriétés et de leurs valeurs s'alignent davantage avec d'autres langages orientés objet comme Java. Dans cette section, nous montrerons comment créer des objets à partir de classes.
Dans de nombreux autres langages, les classes (ou constructeurs) sont clairement distinguées des objets (ou instances). En JavaScript, les classes sont principalement une abstraction par-dessus le mécanisme d'héritage prototypal existant : tous les schémas sont convertibles en héritage basé sur les prototypes. Les classes sont elles-mêmes des valeurs JavaScript normales et possèdent leurs propres chaînes de prototypes. En fait, la plupart des fonctions JavaScript simples peuvent être utilisées comme constructeurs : on utilise l'opérateur new avec une fonction constructeur pour créer un nouvel objet.
Nous allons manipuler dans ce tutoriel le modèle de classes bien abstrait et discuter des sémantiques qu'offrent les classes. Si vous voulez approfondir le système de prototypes sous-jacent, lisez le guide Héritage et chaîne de prototypes.
Ce chapitre suppose que vous êtes déjà quelque peu familier avec JavaScript et que vous avez utilisé des objets ordinaires.
Si vous avez déjà pratiqué JavaScript, ou suivi ce guide, vous avez probablement déjà utilisé des classes, même sans en avoir créé. Par exemple, ceci peut vous sembler familier :
À la première ligne, nous avons créé une instance de la classe Date, appelée grandJour. À la deuxième ligne, nous appelons une méthode toLocaleDateString() sur l'instance grandJour, qui renvoie une chaîne. Ensuite, nous comparons deux nombres : l'un renvoyé par la méthode getTime(), l'autre obtenu directement depuis la classe Date elle‑même, via Date.now().
Date est une classe intégrée de JavaScript. À partir de cet exemple, on peut dégager quelques idées sur ce que font les classes :
Cela correspond à trois fonctionnalités clés des classes :
Les classes sont généralement créées avec des déclarations de classe.
À l'intérieur du corps d'une classe, plusieurs éléments sont possibles.
Si vous venez d'un monde pré‑ES6, vous êtes peut‑être plus habitué·e à utiliser des fonctions comme constructeurs. Le modèle ci‑dessus se traduirait approximativement par le schéma suivant avec des fonctions constructrices :
Note : Les champs et méthodes privés sont de nouvelles fonctionnalités des classes, sans équivalent trivial avec les fonctions constructrices.
Après la déclaration d'une classe, vous pouvez en créer des instances avec l'opérateur new.
Les fonctions constructrices classiques peuvent être appelées avec new ou sans new. En revanche, tenter « d'appeler » une classe sans new produit une erreur.
Contrairement aux déclarations de fonction, les déclarations de classe ne sont pas hissées (ou, selon certaines interprétations, hissées mais soumises à la zone morte temporelle), ce qui signifie que vous ne pouvez pas utiliser une classe avant sa déclaration.
Ce comportement est similaire à celui des variables déclarées avec let et const.
Comme pour les fonctions, les déclarations de classe ont aussi des équivalents sous forme d'expressions.
Les expressions de classe peuvent aussi être nommées. Le nom de l'expression n'est visible qu'à l'intérieur du corps de la classe.
Le rôle le plus important d'une classe est probablement d'agir comme « usine » à objets. Par exemple, lorsque nous utilisons le constructeur Date, nous attendons qu'il nous fournisse un nouvel objet représentant les données de date passées — que nous pouvons ensuite manipuler avec d'autres méthodes exposées par l'instance. Dans les classes, la création d'instances est effectuée par le constructeur.
Par exemple, nous allons créer une classe Color qui représente une couleur précise. Les utilisateur·ice·s créent des couleurs en passant un triplet RGB.
Ouvrez les outils de développement de votre navigateur, collez le code ci‑dessus dans la console, puis créez une instance :
Vous devriez voir une sortie de ce type :
Object { values: (3) […] } values: Array(3) [ 255, 0, 0 ]Vous avez créé avec succès une instance de Color, et cette instance possède une propriété values, qui est un tableau des valeurs RGB passées. C'est à peu près équivalent à :
La syntaxe du constructeur est exactement la même qu'une fonction normale — ce qui signifie que vous pouvez utiliser d'autres syntaxes, comme les paramètres rest :
Chaque appel à new crée une instance différente.
Dans le constructeur d'une classe, la valeur de this pointe vers l'instance nouvellement créée. Vous pouvez lui attribuer des propriétés, ou lire des propriétés existantes (notamment des méthodes, abordées ensuite).
La valeur this est automatiquement renvoyée comme résultat de new. Il est déconseillé de renvoyer une valeur depuis le constructeur — car si vous renvoyez une valeur non primitive, elle deviendra la valeur de l'expression new, et la valeur de this sera ignorée. (Voir la description de new pour plus de détails.)
Si une classe n'a qu'un constructeur, elle ne diffère pas beaucoup d'une fonction usine createX qui crée de simples objets. La force des classes est qu'elles servent de « modèles » assignant automatiquement des méthodes aux instances.
Par exemple, pour les instances de Date, vous pouvez utiliser un ensemble de méthodes pour récupérer différentes informations à partir d'une même valeur de date, comme l'année, le mois, le jour de la semaine, etc. Vous pouvez aussi définir ces valeurs via les variantes setX comme setFullYear.
Pour notre propre classe Color, on peut ajouter une méthode getRed qui renvoie la valeur rouge de la couleur.
Sans méthodes, on pourrait être tenté de définir la fonction dans le constructeur :
Cela fonctionne aussi. Problème : cela crée une nouvelle fonction à chaque création d'instance Color, même si elles font toutes la même chose !
À l'inverse, si vous utilisez une méthode, elle sera partagée entre toutes les instances. Une fonction peut être partagée entre toutes les instances tout en ayant un comportement différent selon l'instance qui l'appelle, car la valeur de this diffère. Si vous vous demandez où est stockée cette méthode : elle est définie sur le prototype de toutes les instances, c'est‑à‑dire Color.prototype, comme expliqué en détail dans Héritage et chaîne de prototypes.
De même, on peut créer une méthode setRed qui définit la composante rouge de la couleur.
Vous vous demandez peut‑être : pourquoi s'embêter avec getRed et setRed alors qu'on peut accéder directement au tableau values sur l'instance ?
Il existe une philosophie en programmation orientée objet appelée « encapsulation ». Cela signifie qu'il ne faut pas accéder à l'implémentation interne d'un objet, mais utiliser des méthodes bien abstraites pour interagir avec lui. Par exemple, si nous décidions soudain de représenter les couleurs en HSL plutôt qu'en RGB :
L'hypothèse des utilisateur·ice·s selon laquelle values représente la valeur RGB s'effondre, et leur logique peut casser. Donc, en tant que créateur·ice d'une classe, vous voudrez masquer la structure de données interne de votre instance à vos utilisateur·ice·s, à la fois pour garder une API propre et pour éviter que leur code ne casse lors de « refactorings inoffensifs ». Dans les classes, cela se fait via les champs privés.
Un champ privé est un identifiant préfixé par #. Le dièse fait partie intégrante du nom, ce qui garantit qu'un champ privé ne peut jamais entrer en collision avec un champ ou une méthode publique. Pour référencer un champ privé où que ce soit dans la classe, vous devez le déclarer dans le corps de la classe (on ne peut pas créer un élément privé à la volée). Hormis cela, un champ privé est globalement équivalent à une propriété normale.
Accéder aux champs privés en dehors de la classe est une erreur de syntaxe précoce. Le langage peut l'empêcher car #privateField est une syntaxe spéciale, ce qui permet une analyse statique de toutes les utilisations de champs privés avant même l'évaluation du code.
Note : Le code exécuté dans la console de Chrome peut accéder à des éléments privés en dehors de la classe. Il s'agit d'un assouplissement spécifique aux DevTools par rapport à la restriction de syntaxe JavaScript.
Les champs privés en JavaScript sont strictement privés (hard private en anglais) : si la classe n'expose pas de méthodes pour accéder à ces champs, il n'existe absolument aucun mécanisme pour les récupérer depuis l'extérieur. Vous pouvez donc refactorer librement les champs privés de votre classe tant que le comportement des méthodes exposées reste inchangé.
Après avoir rendu values privé, on peut enrichir getRed et setRed plutôt que d'en faire de simples relais. Par exemple, on peut vérifier dans setRed que la valeur R est valide :
Si nous laissions la propriété values exposée, les utilisateur·ice·s pourraient aisément contourner ce contrôle en affectant directement values[0] et créer des couleurs invalides. Avec une API bien encapsulée, on rend le code plus robuste et on évite des erreurs logiques en aval.
Une méthode de classe peut lire les champs privés d'autres instances, pourvu qu'elles appartiennent à la même classe.
En revanche, si anotherColor n'est pas une instance de Color, #values n'existe pas. (Même si une autre classe possède un champ privé #values de même nom, il ne référence pas la même chose et n'est pas accessible ici.) Tenter d'accéder à un élément privé inexistant lève une erreur au lieu de renvoyer undefined comme le feraient des propriétés normales. Si vous ne savez pas si un champ privé existe sur un objet et souhaitez y accéder sans try/catch, utilisez l'opérateur in.
Note : Gardez à l'esprit que # est une syntaxe d'identifiant spéciale : vous ne pouvez pas utiliser le nom du champ comme une chaîne. "#values" in anotherColor rechercherait une propriété littéralement nommée "#values", et non un champ privé.
Il existe des limitations avec les éléments privés : un même nom ne peut pas être déclaré deux fois dans une classe, et ils ne peuvent pas être supprimés. Les deux cas génèrent des erreurs de syntaxe précoces.
Les méthodes, accesseurs et mutateurs peuvent aussi être privés. Ils sont utiles lorsqu'un traitement complexe doit rester interne à la classe, sans qu'aucune autre partie du code ne puisse l'appeler.
Par exemple, imaginons des éléments HTML personnalisés devant effectuer une action un peu complexe lors d'un clic/tap/activation. De plus, ces actions doivent être limitées à cette classe, car aucune autre partie du JavaScript ne doit (ni ne devrait) y accéder.
Ici, presque tous les champs et méthodes sont privés à la classe. L'interface exposée au reste du code est donc similaire à celle d'un élément HTML natif. Aucune autre partie du programme ne peut influencer l'interne de Counter.
color.getRed() et color.setRed() permettent de lire et d'écrire la valeur rouge d'une couleur. Si vous venez de langages comme Java, ce motif vous est très familier. Cependant, en JavaScript, il reste peu ergonomique d'utiliser des méthodes pour simplement accéder à une propriété. Les champs d'accesseur permettent de manipuler quelque chose comme s'il s'agissait d'une « vraie propriété » :
On dirait que l'objet a une propriété red — mais en réalité, aucune propriété de ce nom n'existe sur l'instance ! Il n'y a que deux méthodes, préfixées par get et set, qui permettent de les manipuler comme des propriétés.
Si un champ n'a qu'un accesseur mais pas de mutateur, il est effectivement en lecture seule.
En mode strict, la ligne rouge.red = 0 lèvera une erreur de type : « Cannot set property red of #<Color> which has only a getter ». En mode non strict, l'affectation est silencieusement ignorée.
Les champs privés ont leurs homologues publics, qui permettent à chaque instance de posséder une propriété. Les champs sont en général conçus pour être indépendants des paramètres du constructeur.
Les champs publics sont presque équivalents à l'affectation d'une propriété sur this. Par exemple, l'exemple ci‑dessus peut aussi s'écrire :
Avec l'exemple Date, nous avons aussi rencontré la méthode Date.now(), qui renvoie la date courante. Cette méthode n'appartient à aucune instance : elle appartient à la classe elle‑même. Cependant, elle est placée sur la classe Date plutôt qu'exposée comme une fonction globale DateNow(), car elle est surtout utile lorsqu'on manipule des instances de date.
Note : Préfixer les méthodes utilitaires par ce qu'elles manipulent s'appelle « nommage par espace de noms » (namespacing en anglais) et est une bonne pratique. Par exemple, en plus de l'ancienne méthode non préfixée parseInt(), JavaScript a ajouté plus tard la méthode préfixée Number.parseInt() pour indiquer qu'elle concerne les nombres.
Les propriétés statiques regroupent des fonctionnalités de classe définies sur la classe elle‑même, et non sur ses instances. Cela inclut :
Chaque élément possède aussi son équivalent privé. Par exemple, pour notre classe Color, on peut créer une méthode statique qui vérifie si un triplet donné est un RGB valide :
Les propriétés statiques sont très proches de leurs homologues d'instance, sauf que :
Il existe aussi une construction particulière appelée bloc d'initialisation statique, qui est un bloc de code exécuté au premier chargement de la classe.
Les blocs d'initialisation statique sont presque équivalents à l'exécution immédiate d'un code juste après la déclaration de la classe. La seule différence est qu'ils ont accès aux éléments privés statiques.
Une caractéristique clé apportée par les classes (en plus d'une encapsulation ergonomique avec les champs privés) est l'héritage, qui permet à un objet de « réutiliser » une grande partie du comportement d'un autre objet, tout en surchargeant ou enrichissant certaines parties avec sa propre logique.
Par exemple, supposons que notre classe Color doive désormais gérer la transparence. On pourrait être tenté d'ajouter un nouveau champ indiquant la transparence :
Cependant, cela signifie que chaque instance — même la grande majorité qui ne sont pas transparentes (alpha à 1) — devra porter la valeur alpha supplémentaire, ce qui manque d'élégance. De plus, si les fonctionnalités continuent de croître, notre classe Color deviendra volumineuse et difficile à maintenir.
En programmation orientée objet, on créera plutôt une classe dérivée. La classe dérivée a accès à toutes les propriétés publiques de la classe parente. En JavaScript, les classes dérivées se déclarent avec la clause extends, qui indique la classe étendue.
Plusieurs points importants apparaissent immédiatement. D'abord, dans le constructeur, nous appelons super(r, g, b). Le langage impose d'appeler super() avant d'accéder à this. L'appel à super() invoque le constructeur de la classe parente pour initialiser this — ici cela revient grosso modo à this = new Color(r, g, b). Vous pouvez écrire du code avant super(), mais vous ne pouvez pas accéder à this avant super() — le langage interdit l'accès à un this non initialisé.
Une fois la classe parente terminée, la classe dérivée peut appliquer sa logique. Ici, nous ajoutons un champ privé #alpha et fournissons un couple accesseur/mutateur pour l'exposer.
Une classe dérivée hérite de toutes les méthodes de son parent. Par exemple, bien que ColorWithAlpha ne déclare pas l'accesseur get red() lui‑même, vous pouvez toujours accéder à red car ce comportement est fourni par la classe parente :
Les classes dérivées peuvent aussi surcharger des méthodes du parent. Par exemple, toutes les classes héritent implicitement de la classe Object, qui définit des méthodes de base comme toString(). Cependant, la méthode de base toString() est notoirement peu utile, car elle affiche [object Object] dans la plupart des cas :
À la place, notre classe peut la surcharger pour afficher les valeurs RGB de la couleur :
Dans les classes dérivées, vous pouvez accéder aux méthodes de la classe parente via super. Cela permet de construire des méthodes d'enrichissement et d'éviter la duplication de code.
Quand vous utilisez extends, les méthodes statiques s'héritent aussi, donc vous pouvez également les surcharger ou les enrichir.
Les classes dérivées n'ont pas accès aux champs privés de la classe parente — c'est un autre aspect des champs privés « strictement privés ». Les champs privés sont limités au corps de la classe elle‑même et n'accordent l'accès à aucun code extérieur.
Une classe ne peut étendre qu'une seule classe. Cela évite les problèmes de l'héritage multiple comme le problème du diamant. Cependant, du fait de la nature dynamique de JavaScript, il reste possible d'obtenir des effets d'héritage multiple via la composition de classes et les mixin.
Les instances de classes dérivées sont aussi des instances de la classe de base.
Ce guide a été pragmatique jusqu'ici : nous nous concentrons sur comment utiliser les classes, mais une question demeure : pourquoi utiliser une classe ? Réponse : cela dépend.
Les classes introduisent un paradigme, une façon d'organiser votre code. Elles sont le fondement de la programmation orientée objet, basée sur des concepts tels que l'héritage et le polymorphisme (en particulier le polymorphisme par sous‑type). Cependant, de nombreuses personnes sont philosophiquement opposées à certaines pratiques de l'OOP et n'utilisent donc pas les classes.
Par exemple, un aspect qui rend les objets Date tristement célèbres est qu'ils sont mutables.
Mutabilité et état interne sont des aspects importants de l'orienté objet, mais rendent souvent le code difficile à raisonner : une opération apparemment anodine peut avoir des effets de bord inattendus et modifier le comportement ailleurs dans le programme.
Pour réutiliser du code, on a tendance à étendre des classes, ce qui peut créer de grandes hiérarchies d'héritage.
Cependant, il est souvent difficile de décrire proprement l'héritage lorsqu'une classe ne peut étendre qu'une seule autre. Souvent, nous voulons le comportement de plusieurs classes. En Java, cela passe par des interfaces ; en JavaScript, on peut utiliser des mixin. Mais au final, ce n'est pas très pratique.
Côté positif, les classes offrent un moyen puissant d'organiser le code à un niveau plus élevé. Par exemple, sans la classe Color, il faudrait écrire une douzaine de fonctions utilitaires :
Avec des classes, on peut tout regrouper sous l'« espace de noms » Color, ce qui améliore la lisibilité. De plus, l'introduction des champs privés permet de cacher certaines données aux utilisateur·ice·s, créant une API propre.
De manière générale, envisagez d'utiliser des classes lorsque vous voulez créer des objets qui stockent leurs propres données internes et exposent de nombreux comportements. Exemples parmi les classes intégrées de JavaScript :
JavaScript fournit un mécanisme pour organiser votre code de manière objectivement orientée objet, mais l'opportunité et la manière de l'utiliser relèvent entièrement du jugement du·de la programmeur·euse.
Cette page a été modifiée le 8 nov. 2025 par les contributeur·ice·s du MDN.
Votre modèle pour un internet meilleur.
Visitez la société mère à but non lucratif de Mozilla Corporation, la Fondation Mozilla.
Certaines parties de ce contenu sont protégées par le droit d'auteur ©1998—2026 des contributeurs individuels de mozilla.org. Contenu disponible sous une licence Creative Commons.