Les classes introduites par ES2015
ne sont clairement que du sucre syntaxique.
Elles n'apportent rien de plus en terme de fonctionnalité. Toutefois, l'objectif
de cette évolution est de rendre le code plus lisible et plus facilement
accessible.
Oubliez les fonctions anonymes et les prototypes, la sémantique a pris le
relais, comme vous pouvez le constater dans l'exemple de déclaration de la
classe User
:
class User {
// méthode constructor
constructor(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
sayName() {
return `${this.firstname} ${this.lastname}`;
}
}
// instanciation
const user = new User("John", "Doe");
// appel de la méthode sayName()
console.log(user.sayName()); // John Doe
Pour rappel, voici une
façon possible d'écrire
cette classe en es5
:
function User(firstname, lastname) {
if (!(this instanceof User)) {
throw new TypeError("Class constructors cannot be invoked without 'new'");
}
this.firstname = firstname;
this.lastname = lastname;
}
User.prototype.sayName = function() {
return this.firstname + " " + this.lastname;
};
// instanciation
var user = new User("John", "Doe");
// appel de la méthode sayName()
console.log(user.sayName()); // John Doe
Toutes les méthodes s'écrivent comme de simples fonctions à l'intérieur de la classe. Bien entendu, les getter/setter sont toujours de la partie mais bien plus simples à utiliser :
class User {
// constructor
constructor(firstname, lastname, type) {
this.firstname = firstname
this.lastname = lastname
this.type = type
}
// méthode
sayName() {
return `${this.firstname} ${this.lastname}`
}
// getter
get role() {
return this.type
}
// setter
set role(value) {
return this.type = value
}
}
// le `new` est obligatoire pour appeler une classe
const user = new User("John", "Doe", "Contributor")
console.log(user.sayName()) // John Doe
console.log(user.role) // Contributor
user.role = "Owner"
console.log(user.role) // Owner
Pour qu'une sous-classe hérite d'une autre classe on utilisera le mot clé
extends
.
En utilisant notre exemple précédent :
class Contributor extends User {
constructor(firstname, lastname, numberCommit) {
// le mot clé super est utilisé comme super contructeur. Il permet d'appeler
// et d'avoir accès aux méthodes du parent
super(firstname, lastname);
this.numberCommit = numberCommit;
}
sayNameWithCommit() {
// on peut appeler une méthode de la classe parente avec `super.method`
return super.sayName() + " " + this.sayNumberCommit();
}
sayNumberCommit() {
return this.numberCommit;
}
}
// instanciation
const contributor = new Contributor("Jane", "Smith", 10);
// appel de la méthode sayName()
console.log(contributor.sayName());
console.log(contributor.sayNumberCommit());
Rappel : préférez la composition à l'héritage.
Concrètement, tout ce qui est possible de faire avec cette nouvelle notation l'était bien entendu avant.
Au final, on gagne en clarté, en lisibilité et donc en maintenabilité.
À l'heure actuelle, il est nécessaire de passer par un transpileur comme babel pour utiliser les classes.
Une dernière petite chose, le mode strict est automatiquement activé dans le corps des classes et leurs déclarations. Il n'est donc pas nécessaire de le rajouter manuellement.
/* Mode strict initial */
class MyClass {
someMethod() {
/* Ici le mode strict est implicitement activé */
}
}
/* Mode strict inchangé */