You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -55,13 +55,14 @@ rabbit.run(5); // White Rabbit court à la vitesse 5.
55
55
rabbit.hide(); // White Rabbit se cache!
56
56
```
57
57
58
-
L’objet de la classe `Rabbit` a accès aux deux méthodes `Rabbit`, telles que `rabbit.hide()`, ainsi qu'aux méthodes `Animal`, telles que `rabbit.run()`.
58
+
L’objet de la classe `Rabbit` a accès à la fois aux méthodes `Rabbit`, telles que `rabbit.hide()`, et aux méthodes `Animal`, telles que `rabbit.run()`.
59
59
60
60
En interne, le mot clé `extended` fonctionne en utilisant le bon vieux prototype. Il établit `Rabbit.prototype.[[Prototype]]` vers `Animal.prototype`. Donc, si une méthode n'est pas trouvée dans `Rabbit.prototype`, JavaScript le prend de `Animal.prototype`.
61
61
62
62

63
63
64
64
Par exemple, pour trouver la méthode `rabbit.run`, le moteur vérifie (de bas en haut sur l'image) :
65
+
65
66
1. L'objet `rabbit` (n'a pas de `run`).
66
67
2. Son prototype, c'est-à-dire `Rabbit.prototype` (a `hide`, mais pas `run`).
67
68
3. Son prototype, c'est-à-dire (en raison de `extends`) `Animal.prototype`, qui a finalement la méthode `run`.
@@ -71,7 +72,7 @@ Comme nous pouvons nous en rappeler dans le chapitre <info:native-prototypes>, J
71
72
````smart header="Toute expression est autorisée après `extend`"
72
73
La syntaxe de classe permet de spécifier non seulement une classe, mais toute expression après `extend`.
73
74
74
-
Par exemple, un appel de fonction qui génère la classe parent:
75
+
Par exemple, un appel de fonction qui génère la classe parent:
75
76
76
77
```js run
77
78
functionf(phrase) {
@@ -100,8 +101,8 @@ Mais si nous spécifions notre propre méthode dans `Rabbit`, telle que `stop()`
100
101
```js
101
102
class Rabbit extends Animal {
102
103
stop() {
103
-
// ...now this will be used for rabbit.stop()
104
-
// instead of stop() from class Animal
104
+
// ...maintenant ceci sera utilisé pour rabbit.stop()
105
+
// au lieu de stop() de la classe Animal
105
106
}
106
107
}
107
108
```
@@ -113,7 +114,7 @@ Les classes fournissent le mot clé `"super"` pour cela.
113
114
- `super.method(...)` pour appeler une méthode parente.
114
115
- `super(...)` pour appeler un constructeur parent (dans notre constructeur uniquement).
115
116
116
-
Par exemple, laisson rabbit se cacher automatiquement à l’arrêt:
117
+
Par exemple, laisson rabbit se cacher automatiquement à l’arrêt:
117
118
118
119
```js run
119
120
class Animal {
@@ -157,9 +158,9 @@ rabbit.stop(); // White Rabbit reste immobile. White Rabbit se cache!
157
158
Maintenant, `Rabbit` a la méthode `stop` qui appelle le `super.stop()` du parent dans le processus.
158
159
159
160
````smart header="Les fonctions fléchées n'ont pas de `super`"
160
-
Comme mentionné dans le chapitre <info:arrow-functions>, les fonctions fléchées n'ont pas `super`.
161
+
Comme mentionné dans le chapitre <info:arrow-functions>, les fonctions fléchées n'ont pas `super`.
161
162
162
-
Si on y accède, c'est tiré de la fonction externe. Par exemple:
163
+
Si on y accède, c'est tiré de la fonction externe. Par exemple:
163
164
164
165
```js
165
166
class Rabbit extends Animal {
@@ -169,7 +170,7 @@ class Rabbit extends Animal {
169
170
}
170
171
```
171
172
172
-
Le `super` dans la fonction fléchée est le même que dans `stop()`, donc cela fonctionne comme prévu. Si nous spécifions ici une fonction "régulière", il y aurait une erreur:
173
+
Le `super` dans la fonction fléchée est le même que dans `stop()`, donc cela fonctionne comme prévu. Si nous spécifions ici une fonction "régulière", il y aurait une erreur:
173
174
174
175
```js
175
176
// Super inattendu
@@ -183,11 +184,11 @@ Avec les constructeurs, cela devient un peu délicat.
183
184
184
185
Jusqu'à maintenant, `Rabbit` n'avait pas son propre `constructor`.
185
186
186
-
Selon la [spécification](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation), si une classe étend une autre classe et n'a pas de 'constructeur', alors le `constructor` "vide" suivant est généré:
187
+
Selon la [spécification](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation), si une classe étend une autre classe et n'a pas de `constructor`, alors le `constructor` "vide" suivant est généré:
187
188
188
189
```js
189
190
classRabbitextendsAnimal {
190
-
// généré pour l'extension de classes sans propres constructeurs
191
+
// généré pour l'extension de classes sans constructeur propre
191
192
*!*
192
193
constructor(...args) {
193
194
super(...args);
@@ -198,7 +199,7 @@ class Rabbit extends Animal {
198
199
199
200
Comme nous pouvons le constater, il appelle essentiellement le `constructor` du parent en lui passant tous les arguments. Cela se produit si nous n'écrivons pas notre propre constructeur.
200
201
201
-
Ajoutons maintenant un constructeur personnalisé à `Rabbit`. Il spécifiera le `earLength` en plus de `name`:
202
+
Ajoutons maintenant un constructeur personnalisé à `Rabbit`. Il spécifiera le `earLength` en plus de `name`:
202
203
203
204
```js run
204
205
classAnimal {
@@ -228,13 +229,13 @@ let rabbit = new Rabbit("White Rabbit", 10); // Error: this is not defined.
228
229
*/!*
229
230
```
230
231
231
-
Oups! Nous avons une erreur. Maintenant, nous ne pouvons pas créer de lapins. Qu'est-ce qui s'est passé?
232
+
Oups! Nous avons une erreur. Maintenant, nous ne pouvons pas créer de lapins. Qu'est-ce qui s'est passé?
232
233
233
-
La réponse courte est :
234
+
La réponse courte est :
234
235
235
236
-**les constructeurs dans les classes qui héritent doivent appeler `super(...)`, et (!) le faire avant d'utiliser `this`.**
236
237
237
-
...Mais pourquoi? Que se passe t-il ici? En effet, l'exigence semble étrange.
238
+
...Mais pourquoi? Que se passe t-il ici? En effet, l'exigence semble étrange.
238
239
239
240
Bien sûr, il y a une explication. Entrons dans les détails pour que vous compreniez vraiment ce qui se passe.
240
241
@@ -245,7 +246,7 @@ Ce label affecte son comportement avec `new`.
245
246
- Lorsqu'une fonction normale est exécutée avec `new`, elle crée un objet vide et l'assigne à `this`.
246
247
- Mais lorsqu'un constructeur dérivé s'exécute, il ne le fait pas. Il s'attend à ce que le constructeur parent fasse ce travail.
247
248
248
-
Ainsi, un constructeur dérivé doit appeler `super` pour exécuter son constructeur parent (de base), sinon l'objet pour`this` ne sera pas créé. Et nous aurons une erreur.
249
+
Ainsi, un constructeur dérivé doit appeler `super` pour exécuter son constructeur parent (de base), sinon l'objet pour`this` ne sera pas créé. Et nous aurons une erreur.
249
250
250
251
Pour que le constructeur `Rabbit` fonctionne, il doit appeler `super()` avant d'utiliser `this`, comme ici :
251
252
@@ -273,14 +274,13 @@ class Rabbit extends Animal {
273
274
}
274
275
275
276
*!*
276
-
//now fine
277
+
//maintenant ça marche
277
278
let rabbit =newRabbit("White Rabbit", 10);
278
279
alert(rabbit.name); // White Rabbit
279
280
alert(rabbit.earLength); // 10
280
281
*/!*
281
282
```
282
283
283
-
284
284
### Remplacer les champs de classe : une note délicate
285
285
286
286
```warn header="Note avancée"
@@ -328,7 +328,7 @@ Qu'est-ce qui est étrange à ce sujet ?
328
328
329
329
Si ce n'est pas encore clair, veuillez comparer avec les méthodes.
330
330
331
-
Voici le même code, mais au lieu du champ `this.name`, nous appelons la méthode `this.showName()`:
331
+
Voici le même code, mais au lieu du champ `this.name`, nous appelons la méthode `this.showName()`:
332
332
333
333
```js run
334
334
classAnimal {
@@ -353,29 +353,29 @@ new Rabbit(); // rabbit
353
353
*/!*
354
354
```
355
355
356
-
Remarque: maintenant la sortie est différente.
356
+
Remarque: maintenant la sortie est différente.
357
357
358
358
Et c'est ce à quoi nous nous attendons naturellement. Lorsque le constructeur parent est appelé dans la classe dérivée, il utilise la méthode substituée.
359
359
360
-
... Mais ce n'est pas le cas pour les champs de classe. Comme dit, le constructeur parent utilise toujours le champ parent.
360
+
... Mais ce n'est pas le cas pour les champs de classe. Comme nous l'avons dit, le constructeur parent utilise toujours le champ parent.
361
361
362
362
Pourquoi y a-t-il une différence ?
363
363
364
364
Eh bien, la raison est dans l'ordre d'initialisation du champ. Le champ de classe est initialisé :
365
+
365
366
- Avant le constructeur de la classe de base (qui n'étend rien),
366
367
- Immédiatement après `super()` pour la classe dérivée.
367
368
368
369
Dans notre cas, `Rabbit` est la classe dérivée. Il n'y a pas de `constructor()` dedans. Comme dit précédemment, c'est la même chose que s'il y avait un constructeur vide avec seulement `super(...args)`.
369
370
370
-
Ainsi, `new Rabbit()` appelle `super()`, exécutant ainsi le constructeur parent, et (selon la règle pour les classes dérivées) seulement après que ses champs de classe sont initialisés. Au moment de l'exécution du constructeur parent, il n'y a pas encore de champs de classe `Rabbit`, c'est pourquoi les champs `Animal` sont utilisés.
371
+
Ainsi, `new Rabbit()` appelle `super()`, exécutant ainsi le constructeur parent, et (selon la règle pour les classes dérivées) seulement après cela ses champs de classe sont initialisés. Au moment de l'exécution du constructeur parent, il n'y a pas encore de champs de classe `Rabbit`, c'est pourquoi les champs `Animal` sont utilisés.
371
372
372
373
Cette subtile différence entre les champs et les méthodes est propre à JavaScript.
373
374
374
375
Heureusement, ce comportement ne se révèle que si un champ surchargé est utilisé dans le constructeur parent. Ensuite, il peut être difficile de comprendre ce qui se passe, alors nous l'expliquons ici.
375
376
376
377
Si cela devient un problème, on peut le résoudre en utilisant des méthodes ou des getters / setters au lieu de champs.
377
378
378
-
379
379
## Super: les internes, [[HomeObject]]
380
380
381
381
```warn header="Informations avancées"
@@ -386,17 +386,17 @@ Il concerne les mécanismes internes de l'héritage et du "super".
386
386
387
387
Poussons un peu plus loin sous le capot de `super`. Nous y verrons des choses intéressantes.
388
388
389
-
Tout d'abord, d'après tout ce que nous avons appris jusqu'à présent, `super` ne devrait pas fonctionner du tout!
389
+
Tout d'abord, d'après tout ce que nous avons appris jusqu'à présent, `super` ne devrait pas fonctionner du tout!
390
390
391
-
Oui, en effet, demandons-nous comment cela devrait fonctionner techniquement? Lorsqu'une méthode d'objet est exécutée, l'objet actuel est remplacé par `this`. Si nous appelons `super.method()`, le moteur doit obtenir la `méthode` à partir du prototype de l'objet actuel. Mais comment?
391
+
Oui, en effet, demandons-nous comment cela devrait fonctionner techniquement? Lorsqu'une méthode d'objet est exécutée, l'objet actuel est remplacé par `this`. Si nous appelons `super.method()`, le moteur doit obtenir la `methode` à partir du prototype de l'objet actuel. Mais comment?
392
392
393
-
La tâche peut sembler simple, mais elle ne l’est pas. Le moteur connaît l'objet en cours `this`, de sorte qu'il pourrait obtenir la méthode `méthode` parent sous la forme `this.__proto__.Method`. Malheureusement, une telle solution "naïve" ne fonctionnera pas.
393
+
La tâche peut sembler simple, mais elle ne l’est pas. Le moteur connaît l'objet en cours `this`, de sorte qu'il pourrait obtenir la `methode` parent sous la forme `this.__proto__.Method`. Malheureusement, une telle solution "naïve" ne fonctionnera pas.
394
394
395
395
Montrons le problème. Sans les classes, en utilisant des objets simples pour des raisons de simplicité.
396
396
397
397
Si vous ne voulez pas connaître les détails, vous pouvez sauter cette partie et aller en bas à la sous-section `[[HomeObject]]`. Cela ne fera pas de mal. Ou lisez si vous souhaitez comprendre les choses en profondeur.
398
398
399
-
Dans l'exemple ci-dessous, `rabbit.__proto__=animal`. Essayons maintenant: dans `rabbit.eat()` nous appellerons `animal.eat()`, en utilisant `this.__proto__`:
399
+
Dans l'exemple ci-dessous, `rabbit.__proto__=animal`. Essayons maintenant: dans `rabbit.eat()` nous appellerons `animal.eat()`, en utilisant `this.__proto__`:
400
400
401
401
```js run
402
402
let animal = {
@@ -420,11 +420,11 @@ let rabbit = {
420
420
rabbit.eat(); // Rabbit eats.
421
421
```
422
422
423
-
À la ligne `(*)` nous prenons `eat` du prototype (`animal`) et l'appelons dans le contexte de l'objet actuel. Veuillez noter que `.call(this)` est important ici, car un simple `this.__proto__.Eat()` exécuterait le `eat` du parent dans le contexte du prototype et non de l'objet actuel.
423
+
À la ligne `(*)` nous prenons `eat` du prototype (`animal`) et l'appelons dans le contexte de l'objet actuel. Veuillez noter que `.call(this)` est important ici, car un simple `this.__proto__.eat()` exécuterait le `eat` du parent dans le contexte du prototype et non de l'objet actuel.
424
424
425
-
Et dans le code ci-dessus, cela fonctionne réellement comme prévu: nous avons la bonne `alert`.
425
+
Et dans le code ci-dessus, cela fonctionne réellement comme prévu: nous avons la bonne `alert`.
426
426
427
-
Ajoutons maintenant un objet de plus à la chaîne. Nous verrons comment les choses se casse:
427
+
Ajoutons maintenant un objet de plus à la chaîne. Nous verrons comment les choses se cassent :
Le code ne fonctionne plus! Nous pouvons voir l'erreur en essayant d'appeler `longEar.eat()`.
458
+
Le code ne fonctionne plus! Nous pouvons voir l'erreur en essayant d'appeler `longEar.eat()`.
459
459
460
-
Ce n'est peut-être pas si évident, mais si nous suivons l'appel de `longEar.eat()`, nous pouvons voir pourquoi. Dans les deux lignes `(*)` et `(**)`, la valeur de `this` est l'objet actuel (`longEar`). C'est essentiel: toutes les méthodes d'objet obtiennent l'objet actuel sous la forme `this`, pas un prototype ou quelque chose d'autre.
460
+
Ce n'est peut-être pas si évident, mais si nous suivons l'appel de `longEar.eat()`, nous pouvons voir pourquoi. Dans les deux lignes `(*)` et `(**)`, la valeur de `this` est l'objet actuel (`longEar`). C'est essentiel: toutes les méthodes d'objet obtiennent l'objet actuel sous la forme `this`, pas un prototype ou quelque chose d'autre.
461
461
462
-
Ainsi, dans les deux lignes `(*)` et `(**)`, la valeur de `this.__proto__` est exactement la même: `rabbit`. Ils appellent tous deux `rabbit.eat` sans remonter la chaîne, dans une boucle infinie.
462
+
Ainsi, dans les deux lignes `(*)` et `(**)`, la valeur de `this.__proto__` est exactement la même: `rabbit`. Ils appellent tous deux `rabbit.eat` sans remonter la chaîne, dans une boucle infinie.
463
463
464
-
Voici l'image de ce qui se passe:
464
+
Voici l'image de ce qui se passe:
465
465
466
466

467
467
468
468
1. Dans `longEar.eat()`, la ligne `(**)` appelle `rabbit.eat` et lui fournit `this = longEar`.
469
+
469
470
```js
470
471
// dans longEar.eat() nous avons this = longEar
471
472
this.__proto__.eat.call(this) // (**)
@@ -474,7 +475,8 @@ Voici l'image de ce qui se passe:
474
475
// à savoir
475
476
rabbit.eat.call(this);
476
477
```
477
-
2. Ensuite, dans la ligne `(*)` de `rabbit.eat`, nous aimerions passer l'appel encore plus haut dans la chaîne, mais `this = longEar`, donc `this.__proto__.eat` est encore `rabbit.eat`!
478
+
479
+
2. Ensuite, dans la ligne `(*)` de `rabbit.eat`, nous aimerions passer l'appel encore plus haut dans la chaîne, mais `this = longEar`, donc `this.__proto__.eat` est encore `rabbit.eat` !
478
480
479
481
```js
480
482
// dans rabbit.eat() nous avons aussi this = longEar
@@ -491,13 +493,13 @@ Le problème ne peut pas être résolu en utilisant seulement `this`.
491
493
492
494
### `[[HomeObject]]`
493
495
494
-
Pour fournir la solution, JavaScript ajoute une propriété interne spéciale supplémentaire pour les fonctions: `[[HomeObject]]`.
496
+
Pour fournir la solution, JavaScript ajoute une propriété interne spéciale supplémentaire pour les fonctions: `[[HomeObject]]`.
495
497
496
498
Lorsqu'une fonction est spécifiée en tant que méthode de classe ou d'objet, sa propriété `[[HomeObject]]` devient cet objet.
497
499
498
500
Ensuite, `super` l'utilise pour résoudre le prototype parent et ses méthodes.
499
501
500
-
Voyons comment cela fonctionne, d'abord avec les objets simples:
502
+
Voyons comment cela fonctionne, d'abord avec les objets simples:
501
503
502
504
```js run
503
505
let animal = {
@@ -537,9 +539,9 @@ Comme nous l'avons vu précédemment, les fonctions sont généralement "libres"
537
539
538
540
L'existence même de `[[HomeObject]]` viole ce principe, car les méthodes se souviennent de leurs objets. `[[HomeObject]]` ne peut pas être changé, ce lien est donc permanent.
539
541
540
-
Le seul endroit dans la langue où `[[HomeObject]]` est utilisé - est `super`. Donc, si une méthode n'utilise pas `super`, on peut toujours la considérer comme libre et la copier entre les objets. Mais avec `super`, les choses peuvent mal tourner.
542
+
Le seul endroit dans le langage où `[[HomeObject]]` est utilisé est `super`. Donc, si une méthode n'utilise pas `super`, on peut toujours la considérer comme libre et la copier entre les objets. Mais avec `super`, les choses peuvent mal tourner.
541
543
542
-
Voici la démo d'un mauvais résultat de `super` après la copie:
544
+
Voici la démo d'un mauvais résultat de `super` après la copie:
543
545
544
546
```js run
545
547
let animal = {
@@ -577,12 +579,13 @@ tree.sayHi(); // I'm an animal (?!?)
577
579
578
580
Un appel à `tree.sayHi()` indique "I'm an animal". Certainement faux.
579
581
580
-
La raison est simple:
581
-
- Dans la ligne `(*)`, la méthode `tree.sayHi` a été copiée à partir de `rabbit`. Peut-être que nous voulions simplement éviter la duplication de code?
582
+
La raison est simple :
583
+
584
+
- Dans la ligne `(*)`, la méthode `tree.sayHi` a été copiée à partir de `rabbit`. Peut-être que nous voulions simplement éviter la duplication de code ?
582
585
- Son `[[[HomeObject]]` est `rabbit`, comme il a été créé dans `rabbit`. Il n'y a aucun moyen de changer `[[HomeObject]]`.
583
586
- Le code de `tree.sayHi()` a `super.sayHi()` à l'intérieur. Il monte de `rabbit` et prend la méthode de `animal`.
584
587
585
-
Voici le schéma de ce qui se passe:
588
+
Voici le schéma de ce qui se passe:
586
589
587
590

588
591
@@ -592,7 +595,7 @@ Voici le schéma de ce qui se passe:
592
595
593
596
La différence peut être non essentielle pour nous, mais c'est important pour JavaScript.
594
597
595
-
Dans l'exemple ci-dessous, une syntaxe non-méthode est utilisée pour la comparaison. La propriété `[[HomeObject]]` n'est pas définie et l'héritage ne fonctionne pas:
598
+
Dans l'exemple ci-dessous, une syntaxe non-méthode est utilisée pour la comparaison. La propriété `[[HomeObject]]` n'est pas définie et l'héritage ne fonctionne pas:
596
599
597
600
```js run
598
601
let animal = {
@@ -615,15 +618,16 @@ rabbit.eat(); // Error calling super (parce qu'il n'y a pas de [[HomeObject]])
615
618
616
619
## Résumé
617
620
618
-
1. Pour étendre une classe:`class Child extends Parent`:
621
+
1. Pour étendre une classe:`class Child extends Parent`:
619
622
- Cela signifie que `Child.prototype.__proto__` sera `Parent.prototype`, donc les méthodes sont héritées.
620
-
2. Lors du remplacement d'un constructeur:
623
+
2. Lors du remplacement d'un constructeur:
621
624
- Nous devons appeler le constructeur parent en tant que `super()` dans le constructeur `Child` avant d'utiliser `this`.
622
-
3. Lors du remplacement d'une autre méthode:
625
+
3. Lors du remplacement d'une autre méthode:
623
626
- Nous pouvons utiliser `super.method()` dans une méthode `Child` pour appeler la méthode `Parent`.
624
-
4. Internes:
627
+
4. Internes:
625
628
- Les méthodes se souviennent de leur classe/objet dans la propriété interne `[[HomeObject]]`. C'est ainsi que `super` résout les méthodes parent.
626
629
- Il n'est donc pas prudent de copier une méthode avec `super` d'un objet à un autre.
627
630
628
631
Également :
632
+
629
633
- Les fonctions fléchées n'ont pas leurs propre `this` ou `super`, elles s'adaptent donc de manière transparente au contexte environnant.
0 commit comments