Question 1Parcel a static count = 0;. Qu'obtiens-tu en lisant first.count sur une instance first ?
Membres d'instance et static — les valeurs et méthodes de la classe
Comment static place des valeurs et des méthodes sur la classe elle-même : compteurs, méthodes de fabrique à partir de JSON, constantes partagées, et quand préférer les membres d'instance.
Pour numéroter des colis 1, 2, 3 dans l'ordre de leur création, il faut garder quelque part le nombre de colis déjà créés. Le destinataire change d'un colis à l'autre, mais ce compteur et le poids maximal n'ont besoin d'exister qu'une fois, pour tous les colis.
Cet article traite de static, qui place des valeurs et des méthodes sur la classe elle-même plutôt que sur ses instances.
Compter les colis — les champs static
Si tu écris le champ de classe count = 0;, que tu l'augmentes avec this.count += 1 dans le constructor et que tu t'en sers comme numéro, tous les colis reçoivent le numéro 1 : le champ repart de 0 à chaque new.
Un champ static (une propriété déclarée dans la classe avec le mot-clé static, qui n'existe qu'en un seul exemplaire, sur la classe elle-même) se lit et s'écrit via le nom de la classe, comme Parcel.count.
À l'inverse, les propriétés que chaque instance possède, comme this.number, et les méthodes écrites sans static s'appellent des membres d'instance.
class Parcel {
// Nombre de colis créés jusqu'ici, stocké une seule fois sur la classe
static count = 0;
constructor(recipient) {
Parcel.count += 1; // Ajoute 1 au count de la classe
this.number = Parcel.count; // Le count à cet instant devient le numéro du colis
this.recipient = recipient;
}
}
// Crée deux colis
const first = new Parcel("Alice");
const second = new Parcel("Léa");
console.log(first.number, second.number); // 1 2
console.log(Parcel.count); // 2
console.log(first.count); // undefined
static count— 1 après le premiernew, 2 après le second- Se lit et s'écrit avec
Parcel.count
number— 1 (le count à la création)recipient— « Alice »- Pas de
count—first.countvautundefined
number— 2 (le count à la création)recipient— « Léa »
count, dans la boîte de la classe, et aucun dans celles des instances. Le lire depuis une instance donne undefined.Chaque new augmente de 1 le même count, celui de la boîte de la classe, et copie sa valeur du moment dans le number de la nouvelle instance. Même quand count passe ensuite à 2, first.number reste à 1, la valeur reçue à sa création.
Créer des colis à partir de JSON — les méthodes static
Quand tu recharges un colis enregistré, tu obtiens une chaîne JSON qui contient le destinataire et le poids. Si chaque écran qui charge des colis a son propre JSON.parse et son propre new Parcel(...), un changement des noms de clés du JSON t'oblige à corriger tous ces écrans.
Or, au moment de créer un colis à partir du JSON, il n'existe encore aucune instance sur laquelle appeler une méthode. Une méthode static (une méthode définie avec static, que l'on appelle via le nom de la classe sans créer d'instance) s'utilise sous la forme Parcel.fromJSON(text). On parle de méthode de fabrique (factory method) pour une méthode static qui, comme fromJSON, crée et renvoie une instance.
class Parcel {
constructor(recipient, weightKg) {
this.recipient = recipient;
this.weightKg = weightKg;
}
label() {
return `${this.recipient} (${this.weightKg} kg)`;
}
// Reçoit une chaîne JSON, puis crée et renvoie une instance de Parcel
static fromJSON(text) {
const data = JSON.parse(text);
return new Parcel(data.recipient, data.weightKg);
}
}
const text = '{"recipient":"Alice","weightKg":2}';
const parcel = Parcel.fromJSON(text); // Appel via le nom de la classe
console.log(parcel.label()); // Alice (2 kg)
JSON.parse donne un objet simple, avec des propriétés mais sans méthodes. Ce n'est qu'après être passé par new qu'il devient une instance capable d'appeler label().La dernière ligne de fromJSON est new Parcel(...) : le corps du constructor s'exécute donc normalement. Si tu places des vérifications avec throw dans le constructor, les colis rechargés depuis le JSON passent par les mêmes vérifications que ceux saisis à l'écran.
fromJSON ne s'appelle pas sur une instance
Si tu l'appelles sur une instance, comme parcel.fromJSON(text), une TypeError: parcel.fromJSON is not a function est levée. Contrairement à label(), une méthode static ne peut être appelée que via le nom de la classe. Le mécanisme est expliqué dans l'article sur les prototypes.
Une limite fixe pour tous les colis — les constantes static
Le poids maximal de 30 kg sert à la fois dans le message du formulaire de saisie, affiché avant même qu'un colis existe, et dans le calcul de la capacité restante de chaque colis. Si tu mets cette limite dans un champ d'instance, il faut créer un colis sans destinataire ni poids rien que pour afficher le message du formulaire.
Une constante static (un champ static dont la valeur ne change pas pendant l'exécution) s'écrit par convention tout en majuscules, avec des tirets bas entre les mots, comme static MAX_WEIGHT_KG = 30;. Hors de la classe comme à l'intérieur d'une méthode, tu la lis via le nom de la classe : Parcel.MAX_WEIGHT_KG.
class Parcel {
// La limite commune à tous les colis est stockée une seule fois sur la classe avec static
static MAX_WEIGHT_KG = 30;
constructor(weightKg) {
this.weightKg = weightKg;
}
// Combien de kg on peut encore ajouter avant la limite
remainingKg() {
return Parcel.MAX_WEIGHT_KG - this.weightKg;
// Avec this.MAX_WEIGHT_KG - this.weightKg, on obtiendrait NaN
}
}
// Lisible avant même qu'une instance existe
console.log(`Jusqu'à ${Parcel.MAX_WEIGHT_KG} kg par colis`); // Jusqu'à 30 kg par colis
const parcel = new Parcel(12);
console.log(parcel.remainingKg()); // 18
La colonne de droite aboutit à NaN parce que this, dans une méthode, désigne l'instance, et que les constantes static ne se trouvent pas sur l'instance. undefined - 12 ne lève aucune erreur : tu ne t'en rends compte qu'en voyant un poids restant incohérent à l'affichage.
Un nom en majuscules n'empêche pas la réaffectation
Un nom en majuscules n'est qu'une convention qui signifie « ne pas modifier » : si tu écris Parcel.MAX_WEIGHT_KG = 50;, la valeur change. Si tu définis seulement un getter, comme static get MAX_WEIGHT_KG() { return 30; }, toute affectation lève une TypeError (les getters sont présentés dans l'article suivant).
Choisir où placer une valeur — static ou instance
Une fois que tu connais static, il peut sembler pratique de mettre aussi une valeur comme le destinataire sur la classe, pour la lire à un seul endroit, Parcel.recipient. Lors d'un test rapide qui ne crée qu'un seul colis, le même destinataire s'affiche qu'il soit en static ou dans this : tu ne remarques pas la différence.
Pour décider où placer une valeur, demande-toi si elle change d'un objet à l'autre. Une valeur placée dans this existe séparément dans chaque instance créée par new, alors qu'une valeur static n'existe qu'une fois, sur la classe, quel que soit le nombre de colis créés.
// Destinataire gardé en static
class SharedParcel {
static recipient = "";
constructor(recipient) { SharedParcel.recipient = recipient; }
label() { return `Destinataire : ${SharedParcel.recipient}`; }
}
// Destinataire gardé dans l'instance
class Parcel {
constructor(recipient) { this.recipient = recipient; }
label() { return `Destinataire : ${this.recipient}`; }
}
const sharedFirst = new SharedParcel("Alice");
new SharedParcel("Léa");
console.log(sharedFirst.label()); // Destinataire : Léa
const first = new Parcel("Alice");
new Parcel("Léa");
console.log(first.label()); // Destinataire : Alice
sharedFirst n'a pas de destinataire à lui, et label() lit SharedParcel.recipient tel qu'il est au moment de l'appel : il affiche donc « Léa », la dernière valeur affectée. Le tableau ci-dessous récapitule les membres utilisés pour les colis dans cet article : comment écrire chaque type et comment s'en servir.
| Ce qu'on y met | Écriture et usage | Exemples ici |
|---|---|---|
| Valeur propre à chaque objet | Dans this, lue sur l'instance | number / recipient / weightKg |
| Méthode lisant ces valeurs | Sans static, appelée sur l'instance | label() / remainingKg() |
| Valeur unique pour tous | Avec static, lue via la classe | count / MAX_WEIGHT_KG |
| Traitement sans instance | Avec static, appelé via la classe | fromJSON(text) |
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Que se passe-t-il si tu appelles une méthode static sur l'instance parcel, comme parcel.fromJSON(text) ?
Question 3Une classe garde le destinataire dans un champ static. Si tu crées un colis pour « Alice », puis un autre pour « Léa », quel destinataire affiche le label() du premier colis ?