Écrire { nom: "Camille", solde: 0 } suffit pour un objet unique. Dès qu'il en faut trente, tous bâtis sur le même modèle et dotés des mêmes méthodes, recopier la structure devient une source d'incohérences.
new est l'opérateur qui règle ce problème : il fabrique un objet à partir d'un modèle et lui donne accès à tout ce que ce modèle sait faire.
Définition
new précède l'appel d'une class ou d'une fonction constructeur. Il crée un objet vide, le relie au modèle, exécute le constructeur sur cet objet, puis le renvoie. L'objet obtenu est une instance : il possède ses propres données et partage les méthodes du modèle.
class Compte {
constructor(titulaire) {
this.titulaire = titulaire;
this.solde = 0;
}
crediter(montant) {
this.solde += montant;
return this.solde;
}
}
const compte = new Compte("Camille");
console.log(compte.crediter(50)); // 50
console.log(compte instanceof Compte); // trueDeux comptes créés à partir de cette classe auraient chacun leur solde, mais une seule fonction crediter existerait en mémoire, partagée par les deux.
Les quatre étapes, dans l'ordre
L'opérateur fait quatre choses, et savoir lesquelles explique la plupart des comportements étranges rencontrés avec this.
| Étape | Ce qui se passe |
|---|---|
| 1 | Un objet vide est créé |
| 2 | Son prototype est relié à celui du modèle |
| 3 | Le constructeur s'exécute avec this pointant sur cet objet |
| 4 | L'objet est renvoyé, sauf si le constructeur renvoie lui-même un objet |
La troisième étape est la plus importante à retenir : à l'intérieur d'un constructeur, this désigne l'instance en cours de fabrication.
Ce qui arrive quand on l'oublie
Le langage protège les classes, mais pas les fonctions constructeurs héritées de l'ancienne écriture.
class Compte {
constructor(titulaire) { this.titulaire = titulaire; }
}
try {
Compte("Camille"); // sans new
} catch (erreur) {
console.log(erreur.name); // TypeError
}
const flechee = () => {};
try {
new flechee();
} catch (erreur) {
console.log(erreur.name); // TypeError
}Une classe appelée sans l'opérateur lève une erreur immédiate, ce qui est une bonne nouvelle. Une fonction fléchée refuse également d'être construite : elle n'a ni this propre ni prototype. Une fonction ordinaire, en revanche, s'exécute sans broncher et renvoie undefined, silencieusement.
N'employez jamais new devant Number (nombre), String (chaîne de caractères) ou Boolean (booléen). Vous obtenez un objet enveloppant et non une valeur : typeof new Number(5) vaut "object", et la comparaison avec 5 échoue en Égalité stricte (===). Ces fonctions s'appellent sans opérateur, pour convertir.
Questions fréquentes
Faut-il encore utiliser des fonctions constructeurs ?
Pour du code neuf, non : class fait la même chose de façon plus lisible et protège contre l'appel sans opérateur. Les fonctions constructeurs restent très présentes dans le code existant et dans les bibliothèques anciennes, donc savoir les lire garde toute son utilité.
Quelle différence avec Object.create ?
Object.create fabrique un objet en lui donnant un prototype, sans exécuter le moindre constructeur. L'opérateur new fait les deux, et c'est ce qui le rend pratique : la fabrication et l'initialisation tiennent dans une seule expression. La méthode d'Object (objet) sert surtout quand aucune initialisation n'est nécessaire.
Un constructeur peut-il renvoyer autre chose que l'instance ?
Oui, et c'est la quatrième étape du tableau : si le constructeur renvoie explicitement un objet, c'est cet objet que l'appelant reçoit. Renvoyer une valeur simple, un nombre ou une chaîne, n'a en revanche aucun effet, l'instance part quand même.