Aller au contenu principal

Champs de classe publics et privés

· 5 minutes de lecture
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Plusieurs propositions étendent la syntaxe existante des classes JavaScript avec de nouvelles fonctionnalités. Cet article explique la nouvelle syntaxe des champs de classe publics dans V8 v7.2 et Chrome 72, ainsi que la syntaxe des champs de classe privés à venir.

Voici un exemple de code qui crée une instance d'une classe nommée IncreasingCounter :

const counter = new IncreasingCounter();
counter.value;
// affiche 'Récupération de la valeur actuelle !'
// → 0
counter.increment();
counter.value;
// affiche 'Récupération de la valeur actuelle !'
// → 1

Notez que l'accès à la propriété value exécute du code (c'est-à-dire qu'il affiche un message) avant de retourner le résultat. Maintenant demandez-vous, comment implémenteriez-vous cette classe en JavaScript ? 🤔

Syntaxe des classes ES2015

Voici comment IncreasingCounter pourrait être implémenté en utilisant la syntaxe des classes ES2015 :

class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Récupération de la valeur actuelle !');
return this._count;
}
increment() {
this._count++;
}
}

La classe installe le getter value et une méthode increment sur le prototype. Plus intéressant encore, la classe dispose d'un constructeur qui crée une propriété d'instance _count et définit sa valeur par défaut à 0. Nous avons actuellement tendance à utiliser le préfixe de soulignement pour indiquer que _count ne doit pas être utilisé directement par les consommateurs de la classe, mais ce n'est qu'une convention ; il ne s'agit pas vraiment d'une propriété « privée » avec des sémantiques spéciales appliquées par le langage.

const counter = new IncreasingCounter();
counter.value;
// affiche 'Récupération de la valeur actuelle !'
// → 0

// Rien n'empêche les gens de lire ou de modifier la
// propriété d'instance `_count`. 😢
counter._count;
// → 0
counter._count = 42;
counter.value;
// affiche 'Récupération de la valeur actuelle !'
// → 42

Champs de classe publics

La nouvelle syntaxe des champs de classe publics nous permet de simplifier la définition de la classe :

class IncreasingCounter {
_count = 0;
get value() {
console.log('Récupération de la valeur actuelle !');
return this._count;
}
increment() {
this._count++;
}
}

La propriété _count est maintenant joliment déclarée en haut de la classe. Nous n'avons plus besoin d'un constructeur juste pour définir quelques champs. Sympa !

Cependant, le champ _count est toujours une propriété publique. Dans cet exemple particulier, nous souhaitons empêcher les gens d'accéder directement à la propriété.

Champs de classe privés

C'est là que les champs de classe privés interviennent. La nouvelle syntaxe des champs privés est similaire à celle des champs publics, sauf que vous marquez le champ comme étant privé en utilisant #. Vous pouvez considérer le # comme faisant partie du nom du champ :

class IncreasingCounter {
#count = 0;
get value() {
console.log('Récupération de la valeur actuelle !');
return this.#count;
}
increment() {
this.#count++;
}
}

Les champs privés ne sont pas accessibles en dehors du corps de la classe :

const counter = new IncreasingCounter();
counter.#count;
// → SyntaxError
counter.#count = 42;
// → SyntaxError

Propriétés statiques publiques et privées

La syntaxe des champs de classe peut également être utilisée pour créer des propriétés et des méthodes statiques publiques et privées :

class FakeMath {
// `PI` est une propriété statique publique.
static PI = 22 / 7; // Approximatif.

// `#totallyRandomNumber` est une propriété statique privée.
static #totallyRandomNumber = 4;

// `#computeRandomNumber` est une méthode statique privée.
static #computeRandomNumber() {
return FakeMath.#totallyRandomNumber;
}

// `random` est une méthode statique publique (syntaxe ES2015)
// qui utilise `#computeRandomNumber`.
static random() {
console.log('J'ai entendu dire que vous aimez les nombres aléatoires…');
return FakeMath.#computeRandomNumber();
}
}

FakeMath.PI;
// → 3.142857142857143
FakeMath.random();
// affiche 'J'ai entendu dire que vous aimez les nombres aléatoires…'
// → 4
FakeMath.#totallyRandomNumber;
// → SyntaxError
FakeMath.#computeRandomNumber();
// → SyntaxError

Sous-classage simplifié

Les avantages de la syntaxe des champs de classe deviennent encore plus clairs lorsqu'il s'agit de sous-classes qui introduisent des champs supplémentaires. Imaginez la classe de base suivante Animal :

class Animal {
constructor(name) {
this.name = name;
}
}

Pour créer une sous-classe Cat qui introduit une propriété d'instance supplémentaire, il faudrait auparavant appeler super() pour exécuter le constructeur de la classe de base Animal avant de créer la propriété :

class Cat extends Animal {
constructor(name) {
super(name);
this.likesBaths = false;
}
meow() {
console.log('Miaou!');
}
}

C'est beaucoup de code pour simplement indiquer que les chats n'aiment pas les bains. Heureusement, la syntaxe des champs de classe supprime le besoin de tout le constructeur, y compris l'appel maladroit à super() :

class Cat extends Animal {
likesBaths = false;
meow() {
console.log('Miaou!');
}
}

Support de la fonctionnalité

Support des champs de classe publics

Support des champs de classe privés

Support des méthodes et accesseurs privés