Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions 1-js/09-classes/05-extend-natives/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

Les classes intégrées telles que Array, Map et autres sont également extensibles.

Par exemple, ici, `PowerArray` hérite du `Array` natif:
Par exemple, ici, `PowerArray` hérite du `Array` natif :

```js run
// ajoutez-y une méthode supplémentaire
Expand All @@ -24,6 +24,7 @@ alert(filteredArr.isEmpty()); // false
Notez une chose très intéressante. Les méthodes intégrées telles que `filter`, `map` et autres renvoient des nouveaux objets exactement du type hérité `PowerArray`. Leur implémentation interne utilise la propriété d'objet `constructor` pour cela.

Dans l'exemple ci-dessus,

```js
arr.constructor === PowerArray
```
Expand All @@ -34,7 +35,7 @@ Encore plus, nous pouvons personnaliser ce comportement.

Nous pouvons ajouter un accésseur statique spécial `Symbol.species` à la classe. S'il existe, il devrait renvoyer le constructeur que JavaScript utilisera en interne pour créer de nouvelles entités dans `map`, `filter`, etc.

Si nous souhaitons que des méthodes intégrées comme `map` ou `filter` renvoient des tableaux classiques, nous pouvons retourner `Array` dans `Symbol.species`, comme ici:
Si nous souhaitons que des méthodes intégrées comme `map` ou `filter` renvoient des tableaux classiques, nous pouvons retourner `Array` dans `Symbol.species`, comme ici :

```js run
class PowerArray extends Array {
Expand All @@ -57,7 +58,7 @@ alert(arr.isEmpty()); // false
let filteredArr = arr.filter(item => item >= 10);

*!*
// filteredArr n'est pas PowerArray, mais Array
// filteredArr n'est pas un PowerArray, mais un Array
*/!*
alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function
```
Expand All @@ -70,17 +71,17 @@ D'autres collections, telles que `Map` et `Set`, fonctionnent de la même maniè

## Pas d'héritage statique dans les éléments intégrés

Les objets intégrés ont leurs propres méthodes statiques, par exemple `Object.keys`,` Array.isArray` etc.
Les objets intégrés ont leurs propres méthodes statiques, par exemple `Object.keys`, `Array.isArray`, etc.

Comme nous le savons déjà, les classes natives s'étendent les uns des autres. Par exemple, `Array` extends `Object`.
Comme nous le savons déjà, les classes natives s'étendent les uns des autres. Par exemple, `Array` étend (hérite de) `Object`.

Normalement, lorsqu'une classe en étend une autre, les méthodes statiques et non statiques sont héritées. Cela a été expliqué en détail dans le chapitre [](info:static-properties-methods#statics-and-inheritance).

Mais les classes intégrées sont une exception. Ils n'héritent pas les méthodes statiques les uns des autres.
Mais les classes intégrées sont une exception. Elles n'héritent pas des méthodes statiques les unes des autres.

Par exemple, `Array` et `Date` héritent de `Object`, de sorte que leurs instances ont des méthodes issues de `Object.prototype`. Mais `Array.[[Prototype]]` ne fait pas référence à `Object`, il n'y a donc pas, par exemple, de méthode statique `Array.keys()` (ou `Date.keys()`).

Voici la structure d'image pour `Date` et `Object`:
Voici le schéma la structure pour `Date` et `Object` :

![](object-date-inheritance.svg)

Expand Down
84 changes: 43 additions & 41 deletions 1-js/09-classes/06-instanceof/article.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
# Vérification de classe: "instanceof"
# Vérification de classe : "instanceof"

L'opérateur `instanceof` permet de vérifier si un objet appartient à une certaine classe. Il prend également en compte l'héritage.

Une telle vérification peut être nécessaire dans de nombreux cas. Nous l'utilisons ici pour construire une fonction *polymorphique*, celle qui traite les arguments différemment en fonction de leur type.

## L'opérateur instanceof [#ref-instanceof]

La syntaxe est la suivante:
La syntaxe est la suivante :

```js
obj instanceof Class
```

Cela renvoie `true` si `obj` appartient à la `Class` ou à une classe qui en hérite.

Par exemple:
Par exemple :

```js run
class Rabbit {}
let rabbit = new Rabbit();

// est-ce un objet de la classe Rabbit?
// est-ce un objet de la classe Rabbit ?
*!*
alert( rabbit instanceof Rabbit ); // true
*/!*
```

Cela fonctionne aussi avec les fonctions constructeur:
Cela fonctionne aussi avec les fonctions constructeur :

```js run
*!*
Expand All @@ -36,7 +37,7 @@ function Rabbit() {}
alert( new Rabbit() instanceof Rabbit ); // true
```

...Et avec des classes intégrées comme `Array`:
...Et avec des classes intégrées comme `Array` :

```js run
let arr = [1, 2, 3];
Expand All @@ -48,14 +49,14 @@ Veuillez noter que `arr` appartient également à la classe `Object`. C'est parc

Normalement, l’opérateur `instanceof` examine la chaîne prototypale pour la vérification. Nous pouvons également définir une logique personnalisée dans la méthode statique `Symbol.hasInstance`.

L'algorithme de `obj instanceof Class` fonctionne à peu près comme suit:
L'algorithme de `obj instanceof Class` fonctionne à peu près comme suit :

1. S'il existe une méthode statique `Symbol.hasInstance`, appelez-la simplement: `Class[Symbol.hasInstance](obj)`. Cela devrait renvoyer `true` ou `false`, et nous avons terminé. C'est ainsi que nous pouvons personnaliser le comportement de `instanceof`.
1. S'il existe une méthode statique `Symbol.hasInstance`, appelez-la simplement : `Class[Symbol.hasInstance](obj)`. Cela devrait renvoyer `true` ou `false`, et nous avons terminé. C'est ainsi que nous pouvons personnaliser le comportement de `instanceof`.

     Par exemple:
     Par exemple :

```js run
// configuration du contrôle de instanceOf qui suppose que
// configuration du contrôle de instanceof qui suppose que
// tout ce qui a la propriété canEat est un animal
class Animal {
static [Symbol.hasInstance](obj) {
Expand All @@ -68,9 +69,10 @@ let obj = { canEat: true };
alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) est appelée
```

2. La plupart des classes n'ont pas `Symbol.hasInstance`. Dans ce cas, la logique standard est utilisée: `obj instanceOf Class` vérifie si `Class.prototype` est égale à l'un des prototypes de la chaîne prototypale `obj`.
2. La plupart des classes n'ont pas `Symbol.hasInstance`. Dans ce cas, la logique standard est utilisée : `obj instanceof Class` vérifie si `Class.prototype` est égale à l'un des prototypes de la chaîne prototypale `obj`.

En d'autres termes, on compare l'un après l'autre :

En d'autres termes, comparez les uns après les autres:
```js
obj.__proto__ === Class.prototype?
obj.__proto__.__proto__ === Class.prototype?
Expand All @@ -82,7 +84,7 @@ obj.__proto__.__proto__.__proto__ === Class.prototype?

Dans l'exemple ci-dessus, `rabbit.__ proto__ === Rabbit.prototype`, donne donc la réponse immédiatement.

     Dans le cas d'un héritage, le match sera à la deuxième étape:
Dans le cas d'un héritage, la correspondance se fera à la deuxième étape :

```js run
class Animal {}
Expand All @@ -93,23 +95,23 @@ let rabbit = new Rabbit();
alert(rabbit instanceof Animal); // true
*/!*

// rabbit.__proto__ === Animal.prototype (no match)
// rabbit.__proto__ === Animal.prototype (pas de correspondance)
*!*
// rabbit.__proto__.__proto__ === Animal.prototype (match!)
// rabbit.__proto__.__proto__ === Animal.prototype (ça correspond!)
*/!*
```

Voici l'illustration de ce que `rabbit instanceof Animal` compare avec `Animal.prototype`:
Voici l'illustration de ce que `rabbit instanceof Animal` compare avec `Animal.prototype` :

![](instanceof.svg)

À propos, il y a aussi une méthode [objA.isPrototypeOf(objB)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/isPrototypeOf), qui renvoie `true` si `objA` se trouve quelque part dans la chaîne de prototypes pour `objB`. Ainsi, le test de `obj instanceof Class` peut être reformulé comme suit: `Class.prototype.isPrototypeOf(obj) `.
À propos, il y a aussi une méthode [objA.isPrototypeOf(objB)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/isPrototypeOf), qui renvoie `true` si `objA` se trouve quelque part dans la chaîne de prototypes pour `objB`. Ainsi, le test de `obj instanceof Class` peut être reformulé comme suit : `Class.prototype.isPrototypeOf(obj)`.

C'est drôle, mais le constructeur `Class` lui-même ne participe pas au contrôle! Seule la chaîne de prototypes et `Class.prototype` compte.
C'est drôle, mais le constructeur `Class` lui-même ne participe pas au contrôle ! Seule la chaîne de prototypes et `Class.prototype` compte.

Cela peut avoir des conséquences intéressantes lorsque une propriété `prototype` est modifiée après la création de l'objet.
Cela peut avoir des conséquences intéressantes lorsqu'une propriété `prototype` est modifiée après la création de l'objet.

Comme ici:
Comme ici :

```js run
function Rabbit() {}
Expand All @@ -118,15 +120,15 @@ let rabbit = new Rabbit();
// le prototype est changé
Rabbit.prototype = {};

// ...plus un rabbit!
// ...plus un rabbit !
*!*
alert( rabbit instanceof Rabbit ); // false
*/!*
```

## Bonus: Object.prototype.toString pour le type
## Bonus : Object.prototype.toString pour le type

Nous savons déjà que les objets simples sont convertis en chaîne sous la forme `[objet Objet]`:
Nous savons déjà que les objets simples sont convertis en chaîne sous la forme `[objet Objet]` :

```js run
let obj = {};
Expand All @@ -137,32 +139,32 @@ alert(obj.toString()); // la même chose

C'est leur implémentation de `toString`. Mais il existe une fonctionnalité cachée qui rend `toString` beaucoup plus puissant que cela. Nous pouvons l'utiliser comme un `typeof` étendu et une alternative pour `instanceof`.

Cela semble étrange? Effectivement. Démystifions.
Cela semble étrange ? Effectivement. Démystifions.

Par [spécification](https://tc39.github.io/ecma262/#sec-object.prototype.tostring), le `toString` intégré peut être extrait de l'objet et exécuté dans le contexte de toute autre valeur. Et son résultat dépend de cette valeur.

- Pour un nombre, ce sera `[object Number]`
- Pour un booléen, ce sera `[object Boolean]`
- Pour `null`: `[objet Null]`
- Pour `undefined`: `[objet Undefined]`
- Pour les tableaux: `[objet Array]`
- ... etc (personnalisable).
- Pour `null` : `[objet Null]`
- Pour `undefined` : `[objet Undefined]`
- Pour les tableaux : `[objet Array]`
- ... etc. (personnalisable).

Montrons cela:
Montrons cela :

```js run
// copier la méthode toString dans une variable pour plus d'utilité
let objectToString = Object.prototype.toString;

// quel type est-ce?
// quel type est-ce ?
let arr = [];

alert( objectToString.call(arr) ); // [object *!*Array*/!*]
```

Ici nous avons utilisé [call](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Function/call) comme décrit dans le chapitre [](info:call-apply-decorators) exécuter la fonction `objectToString` dans le contexte `this=arr`.
Ici nous avons utilisé [call](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Function/call) comme décrit dans le chapitre [](info:call-apply-decorators) pour exécuter la fonction `objectToString` dans le contexte `this=arr`.

En interne, l'algorithme `toString` examine `this` et renvoie le résultat correspondant. Plus d'exemples:
En interne, l'algorithme `toString` examine `this` et renvoie le résultat correspondant. Plus d'exemples :

```js run
let s = Object.prototype.toString;
Expand All @@ -176,7 +178,7 @@ alert( s.call(alert) ); // [object Function]

Le comportement de Object `toString` peut être personnalisé à l'aide d'une propriété d'objet spéciale `Symbol.toStringTag`.

Par exemple:
Par exemple :

```js run
let user = {
Expand All @@ -199,19 +201,19 @@ alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]

Comme vous pouvez le constater, le résultat est exactement `Symbol.toStringTag` (s'il existe), encapsulé dans `[objet ...]`.

À la fin, nous avons "typeof sur stéroïdes" qui fonctionne non seulement pour les types de données primitifs, mais aussi pour les objets intégrés et peut même être personnalisé.
Au final, nous avons un "typeof sous stéroïdes" qui fonctionne non seulement pour les types de données primitifs, mais aussi pour les objets intégrés et qui peut même être personnalisé.

Nous pouvons utiliser `{}.toString.call` au lieu de `instanceof` pour les objets intégrés lorsque nous voulons obtenir le type sous forme de chaîne plutôt que simplement pour vérifier.
Nous pouvons utiliser `{}.toString.call` au lieu de `instanceof` pour les objets intégrés lorsque nous voulons obtenir le type sous forme de chaîne de caractères plutôt que pour simplement vérifier.

## Résumé

Résumons les méthodes de vérification de type que nous connaissons:
Résumons les méthodes de vérification de type que nous connaissons :

| | fonctionne pour | renvoie |
|---------------|-------------|---------------|
| `typeof` | primitives | string |
| `{}.toString` | primitives, objets intégrés, objets avec `Symbol.toStringTag` | string |
| `instanceof` | objects | true/false |
| | fonctionne pour | renvoie |
|---------------|---------------------------------------------------------------|------------|
| `typeof` | primitives | string |
| `{}.toString` | primitives, objets intégrés, objets avec `Symbol.toStringTag` | string |
| `instanceof` | objects | true/false |

Comme on peut le constater, `{}.toString` est techniquement un `typeof` "plus avancé".

Expand Down
Loading