1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
|
---
title: Classes
slug: Web/JavaScript/Reference/Classes
tags:
- Classes
- ECMAScript 2015
- Intermédiaire
- JavaScript
- Reference
translation_of: Web/JavaScript/Reference/Classes
---
<div>{{JsSidebar("Classes")}}</div>
<p>Les classes JavaScript ont été introduites avec ECMAScript 2015. Elles sont un « sucre syntaxique » par rapport à l'héritage prototypal. En effet, cette syntaxe n'introduit pas un nouveau modèle d'héritage dans JavaScript ! Elle fournit uniquement une syntaxe plus simple pour créer des objets et manipuler l'héritage.</p>
<h2 id="Définir_des_classes">Définir des classes</h2>
<p>En réalité, les classes sont juste des <a href="/fr/docs/Web/JavaScript/Reference/Fonctions">fonctions</a> spéciales. Ainsi, les classes sont définies de la même façon que les fonctions : par déclaration, ou par expression.</p>
<h3 id="Les_déclarations_de_classes">Les déclarations de classes</h3>
<p>Pour utiliser une déclaration de classe simple, on utilisera le mot-clé <code>class</code>, suivi par le nom de la classe que l'on déclare (ici « Rectangle »).</p>
<pre class="brush: js">class Rectangle {
constructor(hauteur, largeur) {
this.hauteur = hauteur;
this.largeur = largeur;
}
}</pre>
<h4 id="Remontée_des_déclarations_(hoisting)">Remontée des déclarations (<em>hoisting</em>)</h4>
<p><a href="/fr/docs/Web/JavaScript/Reference/Instructions/function">Les déclarations de fonctions</a> sont remontées dans le code. En revanche, ce n'est pas le cas pour les déclarations de classes. Ainsi, il est nécessaire de déclarer la classe avant de l'instancier. Dans le cas contraire, on obtient une {{jsxref("ReferenceError")}} :</p>
<pre class="brush: js example-bad">const p = new Rectangle(); // ReferenceError
class Rectangle {}
</pre>
<h3 id="Les_expressions_de_classes">Les expressions de classes</h3>
<p>Il est possible d'utiliser des expressions de classes, nommées ou anonymes. Si on utilise un nom dans l'expression, ce nom ne sera accessible que depuis le corps de la classe via la propriété {{jsxref("Function.name", "name")}} (cette valeur ne sera pas directement accessible pour les instances).</p>
<pre class="brush: js">// anonyme
let Rectangle = class {
constructor(hauteur, largeur) {
this.hauteur = hauteur;
this.largeur = largeur;
}
};
// nommée
let Rectangle = class Rectangle {
constructor(hauteur, largeur) {
this.hauteur = hauteur;
this.largeur = largeur;
}
};
</pre>
<div class="note">
<p><strong>Note :</strong> Les mêmes règles s'appliquent aux expressions de classes en ce qui concerne la remontée (<em>hoisting</em>) des classes (cf. le paragraphe précédent sur les remontées des déclarations de classe).</p>
</div>
<h2 id="Corps_d'une_classe_et_définition_des_méthodes">Corps d'une classe et définition des méthodes</h2>
<p>Le corps d'une classe est la partie contenue entre les accolades. C'est dans cette partie que l'on définit les propriétés d'une classe comme ses méthodes et son constructeur.</p>
<h3 id="Mode_strict">Mode strict</h3>
<p>Le corps des classes, pour les expressions et pour les déclarations de classes, est exécuté en <a href="/fr/docs/Web/JavaScript/Reference/Strict_mode">mode strict</a> (autrement dit, le constructeur, les méthodes statiques, le prototype, les accesseurs (<em>getters</em>) et mutateurs (<em>setters</em>) sont exécutés en mode strict).</p>
<h3 id="Constructeur">Constructeur</h3>
<p>La méthode <code><a href="/fr/docs/Web/JavaScript/Reference/Classes/constructor">constructor</a></code> est une méthode spéciale qui permet de créer et d'initialiser les objet créés avec une classe. Il ne peut y avoir qu'une seule méthode avec le nom "constructor" pour une classe donnée. Si la classe contient plusieurs occurences d'une méthode <code>constructor</code>, cela provoquera une exception {{jsxref("SyntaxError")}}.</p>
<p>Le constructeur ainsi déclaré peut utiliser le mot-clé <code>super</code> afin d'appeler le constructeur de la classe parente.</p>
<h3 id="Méthodes_de_prototype">Méthodes de prototype</h3>
<p>Voir aussi les définitions de méthode.</p>
<pre class="brush: js">class Rectangle {
constructor(hauteur, largeur) {
this.hauteur = hauteur;
this.largeur = largeur;
}
get area() {
return this.calcArea();
}
calcArea() {
return this.largeur * this.hauteur;
}
}
const carré = new Rectangle(10, 10);
console.log(carré.area);</pre>
<h3 id="Méthodes_statiques">Méthodes statiques</h3>
<p>Le mot-clé <code><a href="/fr/docs/Web/JavaScript/Reference/Classes/static">static</a></code> permet de définir une méthode statique pour une classe. Les méthodes statiques sont appelées par rapport à la classe entière et non par rapport à une <a href="/fr/docs/Web/JavaScript/Introduction_à_JavaScript_orienté_objet#L'instance">instance</a> donnée (ces méthodes ne peuvent pas être appelées « depuis » une instance). Ces méthodes sont généralement utilisées sous formes d'utilitaires au sein d'applications.</p>
<pre class="brush: js">class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
console.log(Point.distance(p1, p2));</pre>
<h3 id="Gestion_de_this_pour_le_prototype_et_les_méthodes_statiques">Gestion de <code>this</code> pour le prototype et les méthodes statiques</h3>
<p>Lorsqu'une méthode statique ou une méthode liée au prototype est appelée sans valeur <code>this</code>, celle-ci vaudra <code>undefined</code> au sein de la fonction. Il n'y aura pas d'autodétermination de <code>this</code> (<em>autoboxing</em> en anglais). On aura le même résultat si on invoque ces fonctions dans du code non-strict car les fonctions liées aux classes sont exécutées en mode strict.</p>
<pre class="brush: js">class Animal {
crie() {
return this;
}
static mange() {
return this;
}
}
let obj = new Animal();
obj.crie(); // Animal {}
let crie = obj.crie;
crie(); // undefined
Animal.mange(); // class Animal
let mange = Animal.mange;
mange(); // undefined</pre>
<p>Si on écrit le code avec des fonctions traditionnelles plutôt qu'avec des classes et qu'on utilise le mode non-strict, l'autodétermination de <code>this</code> sera faite en fonction du contexte dans lequel la fonction a été appelée. Si la valeur initiale est <code>undefined</code>, <code>this</code> correspondra à l'objet global.</p>
<p>L'autodétermination de <code>this</code> n'a pas lieu en mode strict, la valeur <code>this</code> est passée telle quelle.</p>
<pre class="brush: js">function Animal() { }
Animal.prototype.crie = function() {
return this;
}
Animal.mange = function() {
return this;
}
let obj = new Animal();
let crie = obj.crie;
crie(); // l'objet global
let mange = Animal.mange;
mange(); // l'objet global
</pre>
<h3 id="Propriétés_des_instances">Propriétés des instances</h3>
<p>Les propriétés des instances doivent être définies dans les méthodes de la classe :</p>
<pre class="brush: js">class Rectangle {
constructor(hauteur, largeur) {
this.hauteur = hauteur;
this.largeur = largeur;
}
}</pre>
<p>Les propriétés statiques ou les données relatives au prototype doivent être définies en dehors de la déclaration comportant le corps de la classe :</p>
<pre class="brush: js">Rectangle.largeurStatique = 20;
Rectangle.prototype.largeurProto = 25;</pre>
<h3 id="Déclarations_de_champs">Déclarations de champs</h3>
<p>{{SeeCompatTable}}</p>
<div class="warning">
<p><strong>Attention !</strong> Les déclarations de champs publics et privés sont une <a href="https://github.com/tc39/proposal-class-fields">fonctionnalité expérimentale actuellement proposée pour être intégrée dans le standard ECMAScript</a>. Elle n'est pas implémentée par la majorité des navigateurs mais on peut émuler cette fonctionnalité en utilisant un système de compilation tel que <a href="https://babeljs.io/">Babel</a>.</p>
</div>
<h4 id="Déclarations_de_champs_publics">Déclarations de champs publics</h4>
<p>En utilisant la syntaxe pour la déclaration des champs, on peut réécrire l'exemple précédent de la façon suivante :</p>
<pre class="brush: js">class Rectangle {
hauteur = 0;
largeur;
constructor(hauteur, largeur) {
this.hauteur = hauteur;
this.largeur = largeur;
}
}</pre>
<p>En déclarant les champs en préalable, il est plus facile de comprendre la classe dans son ensemble. De plus, on s'assure que les champs soient toujours présents.</p>
<p>Comme on peut le voir dans cet exemple, les champs peuvent éventuellement être déclarés avec une valeur par défaut.</p>
<h4 id="Déclarations_de_champs_privés">Déclarations de champs privés</h4>
<p>En utilisant des champs privés, on peut revoir la définition de la façon suivante :</p>
<pre class="brush: js">class Rectangle {
#hauteur = 0;
#largeur;
constructor(hauteur, largeur){
this.#hauteur = hauteur;
this.#largeur = largeur;
}
}</pre>
<p>Si on utilise les champs privés hors de la classe, cela génèrera une erreur. Ces champs ne peuvent être lus ou modifiés que depuis le corps de la classe. En évitant d'exposer des éléments à l'extérieur, on s'assure que les portions de code qui consomment cette classe n'utilise pas ses détails internes et on peut alors maintenir la classe dans son ensemble et modifier les détails internes si besoin.</p>
<div class="note">
<p><strong>Note :</strong> Les champs privés doivent nécessairement être déclarés en premier dans les déclarations de champ.</p>
</div>
<p>Il n'est pas possible de créer des champs privés <em>a posteriori</em> au moment où on leur affecterait une valeur. Autrement dit, il est possible de déclarer une variable normale au moment voulu lorsqu'on lui affecte une valeur tandis que pour les champs privés, ces derniers doivent être déclarés (éventuellement initialisés) en amont, au début du corps de la classe.</p>
<h2 id="Créer_une_sous-classe_avec_extends">Créer une sous-classe avec <code>extends</code></h2>
<p>Le mot-clé <code><a href="/fr/docs/Web/JavaScript/Reference/Classes/extends">extends</a></code>, utilisé dans les déclarations ou les expressions de classes, permet de créer une classe qui hérite d'une autre classe (on parle aussi de « sous-classe » ou de « classe-fille »).</p>
<pre class="brush: js">class Animal {
constructor(nom) {
this.nom = nom;
}
parle() {
console.log(`${this.nom} fait du bruit.`);
}
}
class Chien extends Animal {
constructor(nom) {
super(nom); // appelle le constructeur parent avec le paramètre
}
parle() {
console.log(`${this.nom} aboie.`);
}
}</pre>
<p>Si on déclare un constructeur dans une classe fille, on doit utiliser <code>super()</code> avant <code>this</code>.</p>
<p>On peut également étendre des classes plus <em>traditionnelles</em> basées sur des constructeurs fonctionnels :</p>
<pre class="brush: js">function Animal (nom) {
this.nom = nom;
}
Animal.prototype.crie = function () {
console.log(`${this.nom} fait du bruit.`);
}
class Chien extends Animal {
crie() {
super.crie();
console.log(`${this.nom} aboie.`);
}
}
let c = new Chien('Ida');
c.crie();
// Ida fait du bruit.
// Ida aboie.</pre>
<p>En revanche, les classes ne permettent pas d'étendre des objets classiques non-constructibles. Si on souhaite créer un lien d'héritage en un objet et une classe, on utilisera {{jsxref("Object.setPrototypeOf()")}} :</p>
<pre class="brush: js">const Animal = {
crie() {
console.log(`${this.nom} fait du bruit.`);
}
};
class Chien {
constructor(nom) {
this.nom = nom;
}
crie() {
super.crie();
console.log(`${this.nom} aboie.`);
}
}
Object.setPrototypeOf(Chien.prototype, Animal);
let d = new Chien('Ida');
d.crie();
// Ida fait du bruit
// Ida aboie.</pre>
<h2 id="Le_symbole_species">Le symbole <code>species</code></h2>
<p>Lorsqu'on souhaite renvoyer des objets {{jsxref("Array")}} avec une sous-classe <code>MonArray</code>, on peut utiliser symbole <code>species</code> pour surcharger le constructeur par défaut.</p>
<p>Par exemple, si, lorsqu'on utilise des méthodes comme {{jsxref("Array.map","map()")}} qui renvoient le constructeur par défaut et qu'on veut qu'elles renvoient <code>Array</code> plutôt que <code>MonArray</code>, on utilisera {{jsxref("Symbol.species")}} :</p>
<pre class="brush: js">class MonArray extends Array {
// On surcharge species
// avec le constructeur Array du parent
static get [Symbol.species]() { return Array; }
}
let a = new MonArray(1,2,3);
let mapped = a.map(x => x * x);
console.log(mapped instanceof MonArray); // false
console.log(mapped instanceof Array); // true</pre>
<h2 id="Utiliser_super_pour_la_classe_parente">Utiliser super pour la classe parente</h2>
<p>Le mot-clé <code><a href="/fr/docs/Web/JavaScript/Reference/Opérateurs/super">super</a></code> est utilisé pour appeler les fonctions rattachées à un objet parent.</p>
<pre class="brush: js">class Chat {
constructor(nom) {
this.nom = nom;
}
parler() {
console.log(`${this.nom} fait du bruit.`);
}
}
class Lion extends Chat {
parler() {
super.parler();
console.log(`${this.nom} rugit.`);
}
}
</pre>
<h2 id="Les_mix-ins">Les <em>mix-ins</em></h2>
<p>Les sous-classes abstraites ou <em>mix-ins</em> sont des modèles (<em>templates</em>) pour des classes. Une classe ECMAScript ne peut avoir qu'une seule classe parente et il n'est donc pas possible, par exemple, d'hériter de plusieurs classes dont une classe abstraite. La fonctionnalité dont on souhaite disposer doit être fournie par la classe parente.</p>
<p>Une fonction peut prendre une classe parente en entrée et renvoyer une classe fille qui étend cette classe parente. Cela peut permettre d'émuler les <em>mix-ins</em> avec ECMAScript.</p>
<pre class="brush: js">let calculetteMixin = Base => class extends Base {
calc() { }
};
let aleatoireMixin = Base => class extends Base {
randomiseur() { }
};
</pre>
<p>Une classe utilisant ces <em>mix-ins</em> peut alors être écrite de cette façon :</p>
<pre class="brush: js">class Toto { }
class Truc extends calculetteMixin(aleatoireMixin(Toto)) { }
</pre>
<h2 id="Spécifications">Spécifications</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">Spécification</th>
<th scope="col">État</th>
<th scope="col">Commentaires</th>
</tr>
<tr>
<td>{{SpecName('ES2015', '#sec-class-definitions', 'Class definitions')}}</td>
<td>{{Spec2('ES2015')}}</td>
<td>Définition initiale.</td>
</tr>
<tr>
<td>{{SpecName('ES2016', '#sec-class-definitions', 'Class definitions')}}</td>
<td>{{Spec2('ES2016')}}</td>
<td></td>
</tr>
<tr>
<td>{{SpecName('ES2017', '#sec-class-definitions', 'Class definitions')}}</td>
<td>{{Spec2('ES2017')}}</td>
<td></td>
</tr>
<tr>
<td>{{SpecName('ESDraft', '#sec-class-definitions', 'Class definitions')}}</td>
<td>{{Spec2('ESDraft')}}</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="Compatibilité_des_navigateurs">Compatibilité des navigateurs</h2>
<p>{{Compat("javascript.classes")}}</p>
<h2 id="Utilisation_via_l'ardoise_dans_Firefox">Utilisation via l'ardoise dans Firefox</h2>
<p>Une classe ne peut pas être redéfinie. Si vous testez votre code via l'ardoise JavaScript de Firefox (Outils > Développement web > Ardoise JavaScript) et que vous exécutez à plusieurs reprises votre code avec la définition d'une classe, vous obtiendrez une exception SyntaxError : <em>redeclaration of let <class-name></em>.</p>
<p>Pour relancer une définition, il faut utiliser le menu Exécuter > Recharger et exécuter. À ce sujet, voir le bug {{bug("1428672")}}.</p>
<h2 id="Voir_aussi">Voir aussi</h2>
<ul>
<li><a href="/fr/docs/Web/JavaScript/Reference/Fonctions">Les fonctions</a></li>
<li>{{jsxref("Instructions/class", "Les déclarations de classes","",1)}}</li>
<li>{{jsxref("Opérateurs/class", "Les expressions de classes","",1)}}</li>
<li>{{jsxref("Opérateurs/super", "super")}}</li>
<li><a href="https://tech.mozfr.org/post/2015/07/29/ES6-en-details-%3A-les-classes">Billet sur les classes (traduction en français)</a></li>
<li><a href="https://github.com/tc39/proposal-class-fields">Champs publics et privés pour les classes (proposition de niveau 3)</a></li>
</ul>
|