[ Web Proxy ]
URL:
Viewing: https://developer.mozilla.org/fr/docs/Web/JavaScript/Guide/Using_classes [Back]  [Original]

Utiliser les classes - JavaScript | MDN

Cette page a t traduite partir de l'anglais par la communaut. Vous pouvez contribuer en rejoignant la communaut francophone sur MDN Web Docs.

View in English Always switch to English

Utiliser les classes

JavaScript est un langage fond sur les prototypes : les comportements d'un objet sont dtermins par ses propres proprits et par celles de son prototype. Cependant, avec l'ajout des classes, la cration de hirarchies d'objets et l'hritage de proprits et de leurs valeurs s'alignent davantage avec d'autres langages orients objet comme Java. Dans cette section, nous montrerons comment crer des objets partir de classes.

Dans de nombreux autres langages, les classes (ou constructeurs) sont clairement distingues des objets (ou instances). En JavaScript, les classes sont principalement une abstraction par-dessus le mcanisme d'hritage prototypal existant : tous les schmas sont convertibles en hritage bas sur les prototypes. Les classes sont elles-mmes des valeurs JavaScript normales et possdent leurs propres chanes de prototypes. En fait, la plupart des fonctions JavaScript simples peuvent tre utilises comme constructeurs : on utilise l'oprateur new avec une fonction constructeur pour crer un nouvel objet.

Nous allons manipuler dans ce tutoriel le modle de classes bien abstrait et discuter des smantiques qu'offrent les classes. Si vous voulez approfondir le systme de prototypes sous-jacent, lisez le guide Hritage et chane de prototypes.

Ce chapitre suppose que vous tes dj quelque peu familier avec JavaScript et que vous avez utilis des objets ordinaires.

Dans cet article

Vue d'ensemble des classes

Si vous avez dj pratiqu JavaScript, ou suivi ce guide, vous avez probablement dj utilis des classes, mme sans en avoir cr. Par exemple, ceci peut vous sembler familier :

js
const grandJour = new Date(2019, 6, 19);
console.log(grandJour.toLocaleDateString());
if (grandJour.getTime() < Date.now()) {
  console.log("Il tait une fois...");
}

la premire ligne, nous avons cr une instance de la classe Date, appele grandJour. la deuxime ligne, nous appelons une mthode toLocaleDateString() sur l'instance grandJour, qui renvoie une chane. Ensuite, nous comparons deux nombres : l'un renvoy par la mthode getTime(), l'autre obtenu directement depuis la classe Date ellemme, via Date.now().

Date est une classe intgre de JavaScript. partir de cet exemple, on peut dgager quelques ides sur ce que font les classes :

  • Les classes crent des objets via l'oprateur new.
  • Chaque objet se voit ajouter certaines proprits (donnes ou mthodes) par la classe.
  • La classe conserve aussi des proprits (donnes ou mthodes) pour ellemme, gnralement utilises pour interagir avec les instances.

Cela correspond trois fonctionnalits cls des classes :

  • Constructeur ;
  • Mthodes et champs d'instance ;
  • Mthodes et champs statiques.

Dclarer une classe

Les classes sont gnralement cres avec des dclarations de classe.

js
class MaClasse {
  // corps de classe
}

l'intrieur du corps d'une classe, plusieurs lments sont possibles.

js
class MaClasse {
  // Constructeur
  constructor() {
    // corps du constructeur
  }
  // Champ d'instance
  monChamp = "toto";
  // Mthode d'instance
  maMethode() {
    // corps de maMethode
  }
  // Champ statique
  static monChampStatique = "tata";
  // Mthode statique
  static maMethodeStatique() {
    // corps de maMethodeStatique
  }
  // Bloc statique
  static {
    // code d'initialisation statique
  }
  // Les champs, mthodes, champs statiques et mthodes statiques
  // existent aussi en variantes  prives 
  #monChampPrive = "tata";
}

Si vous venez d'un monde prES6, vous tes peuttre plus habitue utiliser des fonctions comme constructeurs. Le modle cidessus se traduirait approximativement par le schma suivant avec des fonctions constructrices :

js
function MaClasse() {
  this.monChamp = "toto";
  // corps du constructeur
}
MaClasse.monChampStatique = "tata";
MaClasse.maMethodeStatique = function () {
  // corps de maMethodeStatique
};
MaClasse.prototype.maMethode = function () {
  // corps de maMethode
};

(function () {
  // code d'initialisation statique
})();

Note : Les champs et mthodes privs sont de nouvelles fonctionnalits des classes, sans quivalent trivial avec les fonctions constructrices.

Instancier une classe

Aprs la dclaration d'une classe, vous pouvez en crer des instances avec l'oprateur new.

js
const monInterface = new MaClasse();
console.log(monInterface.monChamp); // 'toto'
monInterface.maMethode();

Les fonctions constructrices classiques peuvent tre appeles avec new ou sans new. En revanche, tenter  d'appeler  une classe sans new produit une erreur.

js
const monInterface = MaClasse(); // TypeError: Class constructor MaClasse cannot be invoked without 'new'

Remonte des dclarations de classe

Contrairement aux dclarations de fonction, les dclarations de classe ne sont pas hisses (ou, selon certaines interprtations, hisses mais soumises la zone morte temporelle), ce qui signifie que vous ne pouvez pas utiliser une classe avant sa dclaration.

js
new MaClasse(); // ReferenceError: Cannot access 'MaClasse' before initialization

class MaClasse {}

Ce comportement est similaire celui des variables dclares avec let et const.

Expressions de classe

Comme pour les fonctions, les dclarations de classe ont aussi des quivalents sous forme d'expressions.

js
const MaClasse = class {
  // corps de classe
};

Les expressions de classe peuvent aussi tre nommes. Le nom de l'expression n'est visible qu' l'intrieur du corps de la classe.

js
const MaClasse = class MaClasseNomLong {
  // Corps de classe. Ici, MaClasse et MaClasseNomLong dsignent la mme classe.
};
new MaClasseNomLong(); // ReferenceError: MaClasseNomLong is not defined

Constructeur

Le rle 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 reprsentant les donnes de date passes que nous pouvons ensuite manipuler avec d'autres mthodes exposes par l'instance. Dans les classes, la cration d'instances est effectue par le constructeur.

Par exemple, nous allons crer une classe Color qui reprsente une couleur prcise. Les utilisateurices crent des couleurs en passant un triplet RGB.

js
class Color {
  constructor(r, g, b) {
    // Affecter les valeurs RGB en proprit de `this`.
    this.values = [r, g, b];
  }
}

Ouvrez les outils de dveloppement de votre navigateur, collez le code cidessus dans la console, puis crez une instance :

js
const red = new Color(255, 0, 0);
console.log(red);

Vous devriez voir une sortie de ce type :

Object { values: (3) [] }
  values: Array(3) [ 255, 0, 0 ]

Vous avez cr avec succs une instance de Color, et cette instance possde une proprit values, qui est un tableau des valeurs RGB passes. C'est peu prs quivalent  :

js
function createColor(r, g, b) {
  return {
    values: [r, g, b],
  };
}

La syntaxe du constructeur est exactement la mme qu'une fonction normale ce qui signifie que vous pouvez utiliser d'autres syntaxes, comme les paramtres rest :

js
class Color {
  constructor(...values) {
    this.values = values;
  }
}

const red = new Color(255, 0, 0);
// Cre une instance de la mme forme que cidessus.

Chaque appel new cre une instance diffrente.

js
const red = new Color(255, 0, 0);
const anotherRed = new Color(255, 0, 0);
console.log(red === anotherRed); // false

Dans le constructeur d'une classe, la valeur de this pointe vers l'instance nouvellement cre. Vous pouvez lui attribuer des proprits, ou lire des proprits existantes (notamment des mthodes, abordes ensuite).

La valeur this est automatiquement renvoye comme rsultat de new. Il est dconseill 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 ignore. (Voir la description de new pour plus de dtails.)

js
class MaClasse {
  constructor() {
    this.monChamp = "toto";
    return {};
  }
}

console.log(new MaClasse().monChamp); // undefined

Mthodes d'instance

Si une classe n'a qu'un constructeur, elle ne diffre pas beaucoup d'une fonction usine createX qui cre de simples objets. La force des classes est qu'elles servent de  modles  assignant automatiquement des mthodes aux instances.

Par exemple, pour les instances de Date, vous pouvez utiliser un ensemble de mthodes pour rcuprer diffrentes informations partir d'une mme valeur de date, comme l'anne, le mois, le jour de la semaine, etc. Vous pouvez aussi dfinir ces valeurs via les variantes setX comme setFullYear.

Pour notre propre classe Color, on peut ajouter une mthode getRed qui renvoie la valeur rouge de la couleur.

js
class Color {
  constructor(r, g, b) {
    this.values = [r, g, b];
  }
  getRed() {
    return this.values[0];
  }
}

const red = new Color(255, 0, 0);
console.log(red.getRed()); // 255

Sans mthodes, on pourrait tre tent de dfinir la fonction dans le constructeur :

js
class Color {
  constructor(r, g, b) {
    this.values = [r, g, b];
    this.getRed = function () {
      return this.values[0];
    };
  }
}

Cela fonctionne aussi. Problme : cela cre une nouvelle fonction chaque cration d'instance Color, mme si elles font toutes la mme chose !

js
console.log(new Color().getRed === new Color().getRed); // false

l'inverse, si vous utilisez une mthode, elle sera partage entre toutes les instances. Une fonction peut tre partage entre toutes les instances tout en ayant un comportement diffrent selon l'instance qui l'appelle, car la valeur de this diffre. Si vous vous demandez o est stocke cette mthode : elle est dfinie sur le prototype de toutes les instances, c'estdire Color.prototype, comme expliqu en dtail dans Hritage et chane de prototypes.

De mme, on peut crer une mthode setRed qui dfinit la composante rouge de la couleur.

js
class Color {
  constructor(r, g, b) {
    this.values = [r, g, b];
  }
  getRed() {
    return this.values[0];
  }
  setRed(value) {
    this.values[0] = value;
  }
}

const rouge = new Color(255, 0, 0);
rouge.setRed(0);
console.log(rouge.getRed()); // 0 ; on devrait l'appeler  noir   ce stade !

Champs privs

Vous vous demandez peuttre : pourquoi s'embter avec getRed et setRed alors qu'on peut accder directement au tableau values sur l'instance ?

js
class Color {
  constructor(r, g, b) {
    this.values = [r, g, b];
  }
}

const rouge = new Color(255, 0, 0);
rouge.values[0] = 0;
console.log(rouge.values[0]); // 0

Il existe une philosophie en programmation oriente objet appele  encapsulation . Cela signifie qu'il ne faut pas accder l'implmentation interne d'un objet, mais utiliser des mthodes bien abstraites pour interagir avec lui. Par exemple, si nous dcidions soudain de reprsenter les couleurs en HSL plutt qu'en RGB :

js
class Color {
  constructor(r, g, b) {
    // values est maintenant un tableau HSL !
    this.values = rgbToHSL([r, g, b]);
  }
  getRed() {
    return this.values[0];
  }
  setRed(value) {
    this.values[0] = value;
  }
}

const rouge = new Color(255, 0, 0);
console.log(rouge.values[0]); // 0 ; ce n'est plus 255, car H vaut 0 pour le rouge pur

L'hypothse des utilisateurices selon laquelle values reprsente la valeur RGB s'effondre, et leur logique peut casser. Donc, en tant que crateurice d'une classe, vous voudrez masquer la structure de donnes interne de votre instance vos utilisateurices, 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 privs.

Un champ priv est un identifiant prfix par #. Le dise fait partie intgrante du nom, ce qui garantit qu'un champ priv ne peut jamais entrer en collision avec un champ ou une mthode publique. Pour rfrencer un champ priv o que ce soit dans la classe, vous devez le dclarer dans le corps de la classe (on ne peut pas crer un lment priv la vole). Hormis cela, un champ priv est globalement quivalent une proprit normale.

js
class Color {
  // Dclaration : chaque instance de Color a un champ priv #values.
  #values;
  constructor(r, g, b) {
    this.#values = [r, g, b];
  }
  getRed() {
    return this.#values[0];
  }
  setRed(value) {
    this.#values[0] = value;
  }
}

const rouge = new Color(255, 0, 0);
console.log(rouge.getRed()); // 255

Accder aux champs privs en dehors de la classe est une erreur de syntaxe prcoce. Le langage peut l'empcher car #privateField est une syntaxe spciale, ce qui permet une analyse statique de toutes les utilisations de champs privs avant mme l'valuation du code.

js
console.log(rouge.#values); // SyntaxError: Private field '#values' must be declared in an enclosing class

Note : Le code excut dans la console de Chrome peut accder des lments privs en dehors de la classe. Il s'agit d'un assouplissement spcifique aux DevTools par rapport la restriction de syntaxe JavaScript.

Les champs privs en JavaScript sont strictement privs (hard private en anglais) : si la classe n'expose pas de mthodes pour accder ces champs, il n'existe absolument aucun mcanisme pour les rcuprer depuis l'extrieur. Vous pouvez donc refactorer librement les champs privs de votre classe tant que le comportement des mthodes exposes reste inchang.

Aprs avoir rendu values priv, on peut enrichir getRed et setRed plutt que d'en faire de simples relais. Par exemple, on peut vrifier dans setRed que la valeur R est valide :

js
class Color {
  #values;
  constructor(r, g, b) {
    this.#values = [r, g, b];
  }
  getRed() {
    return this.#values[0];
  }
  setRed(value) {
    if (value < 0 || value > 255) {
      throw new RangeError("Invalid R value");
    }
    this.#values[0] = value;
  }
}

const rouge = new Color(255, 0, 0);
rouge.setRed(1000); // RangeError: Invalid R value

Si nous laissions la proprit values expose, les utilisateurices pourraient aisment contourner ce contrle en affectant directement values[0] et crer des couleurs invalides. Avec une API bien encapsule, on rend le code plus robuste et on vite des erreurs logiques en aval.

Une mthode de classe peut lire les champs privs d'autres instances, pourvu qu'elles appartiennent la mme classe.

js
class Color {
  #values;
  constructor(r, g, b) {
    this.#values = [r, g, b];
  }
  redDifference(anotherColor) {
    // #values n'a pas besoin d'tre lu depuis this :
    // on peut accder aux champs privs d'autres instances
    // de la mme classe.
    return this.#values[0] - anotherColor.#values[0];
  }
}

const rouge = new Color(255, 0, 0);
const crimson = new Color(220, 20, 60);
rouge.redDifference(crimson); // 35

En revanche, si anotherColor n'est pas une instance de Color, #values n'existe pas. (Mme si une autre classe possde un champ priv #values de mme nom, il ne rfrence pas la mme chose et n'est pas accessible ici.) Tenter d'accder un lment priv inexistant lve une erreur au lieu de renvoyer undefined comme le feraient des proprits normales. Si vous ne savez pas si un champ priv existe sur un objet et souhaitez y accder sans try/catch, utilisez l'oprateur in.

js
class Color {
  #values;
  constructor(r, g, b) {
    this.#values = [r, g, b];
  }
  redDifference(anotherColor) {
    if (!(#values in anotherColor)) {
      throw new TypeError("Color instance expected");
    }
    return this.#values[0] - anotherColor.#values[0];
  }
}

Note : Gardez l'esprit que # est une syntaxe d'identifiant spciale : vous ne pouvez pas utiliser le nom du champ comme une chane. "#values" in anotherColor rechercherait une proprit littralement nomme "#values", et non un champ priv.

Il existe des limitations avec les lments privs : un mme nom ne peut pas tre dclar deux fois dans une classe, et ils ne peuvent pas tre supprims. Les deux cas gnrent des erreurs de syntaxe prcoces.

js
class MauvaisesIdees {
  #prenom;
  #prenom; // erreur de syntaxe ici
  #nom;
  constructor() {
    delete this.#nom; // erreur de syntaxe galement
  }
}

Les mthodes, accesseurs et mutateurs peuvent aussi tre privs. 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 lments HTML personnaliss devant effectuer une action un peu complexe lors d'un clic/tap/activation. De plus, ces actions doivent tre limites cette classe, car aucune autre partie du JavaScript ne doit (ni ne devrait) y accder.

js
class Counter extends HTMLElement {
  #xValue = 0;
  constructor() {
    super();
    this.onclick = this.#clicked.bind(this);
  }
  get #x() {
    return this.#xValue;
  }
  set #x(value) {
    this.#xValue = value;
    window.requestAnimationFrame(this.#render.bind(this));
  }
  #clicked() {
    this.#x++;
  }
  #render() {
    this.textContent = this.#x.toString();
  }
  connectedCallback() {
    this.#render();
  }
}

customElements.define("num-counter", Counter);

Ici, presque tous les champs et mthodes sont privs la classe. L'interface expose au reste du code est donc similaire celle d'un lment HTML natif. Aucune autre partie du programme ne peut influencer l'interne de Counter.

Champs d'accesseur

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 trs familier. Cependant, en JavaScript, il reste peu ergonomique d'utiliser des mthodes pour simplement accder une proprit. Les champs d'accesseur permettent de manipuler quelque chose comme s'il s'agissait d'une  vraie proprit  :

js
class Color {
  constructor(r, g, b) {
    this.values = [r, g, b];
  }
  get red() {
    return this.values[0];
  }
  set red(value) {
    this.values[0] = value;
  }
}

const rouge = new Color(255, 0, 0);
rouge.red = 0;
console.log(rouge.red); // 0

On dirait que l'objet a une proprit red mais en ralit, aucune proprit de ce nom n'existe sur l'instance ! Il n'y a que deux mthodes, prfixes par get et set, qui permettent de les manipuler comme des proprits.

Si un champ n'a qu'un accesseur mais pas de mutateur, il est effectivement en lecture seule.

js
class Color {
  constructor(r, g, b) {
    this.values = [r, g, b];
  }
  get red() {
    return this.values[0];
  }
}

const rouge = new Color(255, 0, 0);
rouge.red = 0;
console.log(rouge.red); // 255

En mode strict, la ligne rouge.red = 0 lvera une erreur de type :  Cannot set property red of #<Color> which has only a getter . En mode non strict, l'affectation est silencieusement ignore.

Champs publics

Les champs privs ont leurs homologues publics, qui permettent chaque instance de possder une proprit. Les champs sont en gnral conus pour tre indpendants des paramtres du constructeur.

js
class MaClasse {
  nombreAleatoire = Math.random();
}
console.log(new MaClasse().nombreAleatoire); // 0.5
console.log(new MaClasse().nombreAleatoire); // 0.3

Les champs publics sont presque quivalents l'affectation d'une proprit sur this. Par exemple, l'exemple cidessus peut aussi s'crire :

js
class MaClasse {
  constructor() {
    this.nombreAleatoire = Math.random();
  }
}

Proprits statiques

Avec l'exemple Date, nous avons aussi rencontr la mthode Date.now(), qui renvoie la date courante. Cette mthode n'appartient aucune instance : elle appartient la classe ellemme. Cependant, elle est place sur la classe Date plutt qu'expose comme une fonction globale DateNow(), car elle est surtout utile lorsqu'on manipule des instances de date.

Note : Prfixer les mthodes 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 mthode non prfixe parseInt(), JavaScript a ajout plus tard la mthode prfixe Number.parseInt() pour indiquer qu'elle concerne les nombres.

Les proprits statiques regroupent des fonctionnalits de classe dfinies sur la classe ellemme, et non sur ses instances. Cela inclut :

  • Les mthodes statiques
  • Les champs statiques
  • Les accesseurs et mutateurs statiques

Chaque lment possde aussi son quivalent priv. Par exemple, pour notre classe Color, on peut crer une mthode statique qui vrifie si un triplet donn est un RGB valide :

js
class Color {
  static isValid(r, g, b) {
    return r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255;
  }
}

Color.isValid(255, 0, 0); // true
Color.isValid(1000, 0, 0); // false

Les proprits statiques sont trs proches de leurs homologues d'instance, sauf que :

  • Elles sont prfixes par static, et
  • Elles ne sont pas accessibles depuis les instances.
js
console.log(new Color(0, 0, 0).isValid); // undefined

Il existe aussi une construction particulire appele bloc d'initialisation statique, qui est un bloc de code excut au premier chargement de la classe.

js
class MaClasse {
  static {
    MaClasse.maProprieteStatique = "toto";
  }
}

console.log(MaClasse.maProprieteStatique); // 'toto'

Les blocs d'initialisation statique sont presque quivalents l'excution immdiate d'un code juste aprs la dclaration de la classe. La seule diffrence est qu'ils ont accs aux lments privs statiques.

Extensions et hritage

Une caractristique cl apporte par les classes (en plus d'une encapsulation ergonomique avec les champs privs) est l'hritage, qui permet un objet de  rutiliser  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 dsormais grer la transparence. On pourrait tre tent d'ajouter un nouveau champ indiquant la transparence :

js
class Color {
  #values;
  constructor(r, g, b, a = 1) {
    this.#values = [r, g, b, a];
  }
  get alpha() {
    return this.#values[3];
  }
  set alpha(value) {
    if (value < 0 || value > 1) {
      throw new RangeError("Alpha value must be between 0 and 1");
    }
    this.#values[3] = value;
  }
}

Cependant, cela signifie que chaque instance mme la grande majorit qui ne sont pas transparentes (alpha 1) devra porter la valeur alpha supplmentaire, ce qui manque d'lgance. De plus, si les fonctionnalits continuent de crotre, notre classe Color deviendra volumineuse et difficile maintenir.

En programmation oriente objet, on crera plutt une classe drive. La classe drive a accs toutes les proprits publiques de la classe parente. En JavaScript, les classes drives se dclarent avec la clause extends, qui indique la classe tendue.

js
class ColorWithAlpha extends Color {
  #alpha;
  constructor(r, g, b, a) {
    super(r, g, b);
    this.#alpha = a;
  }
  get alpha() {
    return this.#alpha;
  }
  set alpha(value) {
    if (value < 0 || value > 1) {
      throw new RangeError("Alpha value must be between 0 and 1");
    }
    this.#alpha = value;
  }
}

Plusieurs points importants apparaissent immdiatement. D'abord, dans le constructeur, nous appelons super(r, g, b). Le langage impose d'appeler super() avant d'accder 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 accder this avant super() le langage interdit l'accs un this non initialis.

Une fois la classe parente termine, la classe drive peut appliquer sa logique. Ici, nous ajoutons un champ priv #alpha et fournissons un couple accesseur/mutateur pour l'exposer.

Une classe drive hrite de toutes les mthodes de son parent. Par exemple, bien que ColorWithAlpha ne dclare pas l'accesseur get red() luimme, vous pouvez toujours accder red car ce comportement est fourni par la classe parente :

js
const couleur = new ColorWithAlpha(255, 0, 0, 0.5);
console.log(couleur.red); // 255

Les classes drives peuvent aussi surcharger des mthodes du parent. Par exemple, toutes les classes hritent implicitement de la classe Object, qui dfinit des mthodes de base comme toString(). Cependant, la mthode de base toString() est notoirement peu utile, car elle affiche [object Object] dans la plupart des cas :

js
console.log(red.toString()); // [object Object]

la place, notre classe peut la surcharger pour afficher les valeurs RGB de la couleur :

js
class Color {
  #values;
  // 
  toString() {
    return this.#values.join(", ");
  }
}

console.log(new Color(255, 0, 0).toString()); // '255, 0, 0'

Dans les classes drives, vous pouvez accder aux mthodes de la classe parente via super. Cela permet de construire des mthodes d'enrichissement et d'viter la duplication de code.

js
class ColorWithAlpha extends Color {
  #alpha;
  // 
  toString() {
    // Appeler la toString() parente et complter sa valeur de retour
    return `${super.toString()}, ${this.#alpha}`;
  }
}

console.log(new ColorWithAlpha(255, 0, 0, 0.5).toString()); // '255, 0, 0, 0.5'

Quand vous utilisez extends, les mthodes statiques s'hritent aussi, donc vous pouvez galement les surcharger ou les enrichir.

js
class ColorWithAlpha extends Color {
  // 
  static isValid(r, g, b, a) {
    // Appeler isValid() du parent et complter le rsultat
    return super.isValid(r, g, b) && a >= 0 && a <= 1;
  }
}

console.log(ColorWithAlpha.isValid(255, 0, 0, -1)); // false

Les classes drives n'ont pas accs aux champs privs de la classe parente c'est un autre aspect des champs privs  strictement privs . Les champs privs sont limits au corps de la classe ellemme et n'accordent l'accs aucun code extrieur.

js
class ColorWithAlpha extends Color {
  log() {
    console.log(this.#values); // SyntaxError: Private field '#values' must be declared in an enclosing class
  }
}

Une classe ne peut tendre qu'une seule classe. Cela vite les problmes de l'hritage multiple comme le problme du diamant. Cependant, du fait de la nature dynamique de JavaScript, il reste possible d'obtenir des effets d'hritage multiple via la composition de classes et les mixin.

Les instances de classes drives sont aussi des instances de la classe de base.

js
const color = new ColorWithAlpha(255, 0, 0, 0.5);
console.log(color instanceof Color); // true
console.log(color instanceof ColorWithAlpha); // true

Pourquoi des classes ?

Ce guide a t pragmatique jusqu'ici : nous nous concentrons sur comment utiliser les classes, mais une question demeure : pourquoi utiliser une classe ? Rponse : cela dpend.

Les classes introduisent un paradigme, une faon d'organiser votre code. Elles sont le fondement de la programmation oriente objet, base sur des concepts tels que l'hritage et le polymorphisme (en particulier le polymorphisme par soustype). Cependant, de nombreuses personnes sont philosophiquement opposes certaines pratiques de l'OOP et n'utilisent donc pas les classes.

Par exemple, un aspect qui rend les objets Date tristement clbres est qu'ils sont mutables.

js
function incrementDay(date) {
  return date.setDate(date.getDate() + 1);
}
const date = new Date(); // 2019-06-19
const newDay = incrementDay(date);
console.log(newDay); // 2019-06-20
// L'ancienne date est modifie aussi !?
console.log(date); // 2019-06-20

Mutabilit et tat interne sont des aspects importants de l'orient objet, mais rendent souvent le code difficile raisonner : une opration apparemment anodine peut avoir des effets de bord inattendus et modifier le comportement ailleurs dans le programme.

Pour rutiliser du code, on a tendance tendre des classes, ce qui peut crer de grandes hirarchies d'hritage.

Un arbre d'hritage POO typique, avec cinq classes et trois niveaux [Un arbre d'hritage POO typique, avec cinq classes et trois niveaux]

Cependant, il est souvent difficile de dcrire proprement l'hritage 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 trs pratique.

Ct 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 :

js
function isRed(color) {
  return color.red === 255;
}
function isValidColor(color) {
  return (
    color.red >= 0 &&
    color.red <= 255 &&
    color.green >= 0 &&
    color.green <= 255 &&
    color.blue >= 0 &&
    color.blue <= 255
  );
}
// 

Avec des classes, on peut tout regrouper sous l' espace de noms  Color, ce qui amliore la lisibilit. De plus, l'introduction des champs privs permet de cacher certaines donnes aux utilisateurices, crant une API propre.

De manire gnrale, envisagez d'utiliser des classes lorsque vous voulez crer des objets qui stockent leurs propres donnes internes et exposent de nombreux comportements. Exemples parmi les classes intgres de JavaScript :

  • Les classes Map et Set stockent des collections d'lments et permettent d'y accder par cl avec get(), set(), has(), etc.
  • La classe Date stocke une date sous forme d'horodatage Unix (un nombre) et permet de la formater, la mettre jour et lire des composants individuels de date.
  • La classe Error stocke des informations sur une exception particulire, y compris le message d'erreur, la pile d'appels, la cause, etc. C'est l'une des rares classes dotes d'une hirarchie riche : plusieurs classes intgres comme TypeError et ReferenceError tendent Error. Pour les erreurs, cet hritage permet d'affiner la smantique : chaque classe d'erreur reprsente un type spcifique, facilement vrifiable avec instanceof.

JavaScript fournit un mcanisme pour organiser votre code de manire objectivement oriente objet, mais l'opportunit et la manire de l'utiliser relvent entirement du jugement dude la programmeureuse.


Web Proxy Viewer  |  New URL  |  Original Page