forked from javascript-tutorial/fr.javascript.info
-
Notifications
You must be signed in to change notification settings - Fork 0
/
=
238 lines (137 loc) · 8.83 KB
/
=
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
# Chaînage optionnel '?.'
[recent browser="new"]
Le chaînage optionnel `?.` Est un moyen sécurisé d'accéder aux propriétés d'objet imbriquées, même si une propriété intermédiaire n'existe pas.
## Le problème de la "propriété non existante"
Si vous venez de commencer à lire le tutoriel et à apprendre JavaScript, peut-être que le problème ne vous a pas encore touché, mais c'est assez courant.
À titre d'exemple, disons que nous avons des objets `user` qui contiennent les informations sur nos utilisateurs.
La plupart de nos utilisateurs ont des adresses dans la propriété `user.address`, avec la rue `user.address.street`, mais certains ne les ont pas fournies.
Dans ce cas, lorsque nous essayons d'obtenir `user.address.street`, et que l'utilisateur se trouve sans adresse, nous obtenons une erreur :
```js run
let user = {}; // un utilisateur sans propriété "address"
alert(user.address.street); // Error!
```
C'est le résultat attendu. JavaScript fonctionne comme ça. Comme `user.address` est `undefined`, une tentative d'obtention de `user.address.street` échoue avec une erreur.
Dans de nombreux cas pratiques, nous préférerions obtenir `undefined` au lieu d'une erreur ici (signifiant "pas de rue").
... Et un autre exemple. Dans le développement Web, nous pouvons obtenir un objet qui correspond à un élément de page Web à l'aide d'un appel de méthode spécial, tel que `document.querySelector('.elem')`, et il renvoie `null` lorsqu'il n'y a pas ce type d'élément.
```js run
// document.querySelector('.elem') est nul s'il n'y a pas d'élément
let html = document.querySelector('.elem').innerHTML; // error if it's null
```
Encore une fois, si l'élément n'existe pas, nous obtiendrons une erreur lors de l'accès à `.innerHTML` de` null`. Et dans certains cas, lorsque l'absence de l'élément est normale, nous aimerions éviter l'erreur et accepter simplement `html = null` comme résultat.
Comment peut-on le faire ?
La solution évidente serait de vérifier la valeur en utilisant `if` ou l'opérateur conditionnel `?`, Avant d'accéder à sa propriété, comme ceci :
```js
let user = {};
alert(user.address ? user.address.street : undefined);
```
Cela fonctionne, il n'y a pas d'erreur ... Mais c'est assez inélégant. Comme vous pouvez le voir, `"user.address"` apparaît deux fois dans le code. Pour des propriétés plus profondément imbriquées, cela devient un problème car plus de répétitions sont nécessaires.
Par exemple. essayons d'obtenir `user.address.street.name`.
Nous devons vérifier à la fois `user.address` et `user.address.street` :
```js
let user = {}; // l'utilisateur n'a pas d'adresse
alert(user.address ? user.address.street ? user.address.street.name : null : null);
```
C'est juste horrible, on peut même avoir des problèmes pour comprendre un tel code.
Ne vous en souciez même pas, car il existe une meilleure façon de l'écrire, en utilisant l'opérateur `&&` :
```js run
let user = {}; // l'utilisateur n'a pas d'adresse
alert( user.address && user.address.street && user.address.street.name ); // undefined (no error)
```
Et le chemin complet vers la propriété garantit que tous les composants existent (sinon, l'évaluation s'arrête), mais n'est pas non plus idéal.
Comme vous pouvez le voir, les noms de propriétés sont toujours dupliqués dans le code. Par exemple. dans le code ci-dessus, `user.address` apparaît trois fois.
C'est pourquoi le chaînage facultatif `?.` A été ajouté au langage. Pour résoudre ce problème une fois pour toutes !
ET l'ensemble du chemin d'accès à la propriété garantit que tous les composants existent (sinon, l'évaluation s'arrête), mais est difficile à écrire.
## Chaînage optionnel
Le chaînage facultatif `?.` arrête l'évaluation et renvoie `undefined` si la partie avant `?.` est `undefined` ou `null` et renvoie cette partie.
**Plus loin dans cet article, par souci de brièveté, nous dirons que quelque chose "existe" si ce n'est pas "null" et non "undefined".**
En d'autres termes, `value?.prop` :
- est identique à `value.prop` si `value` existe,
- sinon (lorsque `value` est `undefined/nul`), il renvoie `undefined`.
Voici le moyen sûr d'accéder à `user.address.street` en utilisant `?.` :
```js run
let user = {}; // l'utilisateur n'a pas d'adresse
alert( user?.address?.street ); // undefined (no error)
```
Le code est court et propre, il n'y a aucune duplication.
La lecture de l'adresse avec `user?.address` fonctionne même si l'objet `user` n'existe pas :
```js run
let user = null;
alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
```
Remarque: la syntaxe `?.` Rend facultative la valeur qui la précède, mais pas plus.
Par exemple. dans `user?.address.street.name` le `?.` permet à `user` d'être `null/undefined`, mais c'est tout ce qu'il fait. D'autres propriétés sont accessibles de manière régulière. Si nous voulons que certains d'entre eux soient optionnels, alors nous devrons remplacer plus de `.` par `?.`.
```warn header="N'abusez pas du chaînage optionnel"
Nous ne devrions utiliser `?.` que là où il est normal que quelque chose n'existe pas.
Par exemple, si selon notre logique de codage, l'objet `user` doit exister, mais que `address` est facultatif, alors nous devrions écrire `user.address?.street`, mais pas `user?.address?.street`.
```
````warn header="La variable avant `?.` doit être déclarée"
S'il n'y a pas du tout de variable `user`, alors `user?.anything` déclenche une erreur :
```js run
// ReferenceError: user is not defined
user?.address;
```
La variable doit être déclarée (par exemple `let/const/var user` ou en tant que paramètre de fonction). Le chaînage facultatif ne fonctionne que pour les variables déclarées.
````
## Court-circuit
Comme il a été dit précédemment, le `?.` arrête immédiatement ("court-circuite") l'évaluation si la partie gauche n'existe pas.
Donc, s'il y a d'autres appels de fonction ou effets secondaires, ils ne se produisent pas :
Par exemple :
```js run
let user = null;
let x = 0;
user?.sayHi(x++); // pas de "sayHi", donc l'exécution n'atteint pas x++
alert(x); // 0, la valeur n'est pas incrémenté
```
## Autres variantes : ?.(), ?.[]
`?.` n'est pas un opérateur, mais une construction syntaxique particulière qui fonctionne aussi avec les appels de fonction et les crochets.
Par exemple, `?.()` est utilisé pour exécuter une fonction seulement si elle existe.
Ainsi dans cet exemple la méthode `admin` n'existe pas pour tout le monde :
```js run
let userAdmin = {
admin() {
alert("I am admin");
}
};
let userGuest = {};
*!*
userAdmin.admin?.(); // I am admin
*/!*
*!*
userGuest.admin?.(); // nothing (no such method)
*/!*
```
Ici, dans les deux lignes, nous utilisons d'abord le point (`user1.admin`) pour obtenir la propriété `admin`, car l'objet utilisateur doit exister, donc il peut être lu en toute sécurité.
Puis `?.()` Vérifie la partie gauche : si la fonction admin existe, alors elle s'exécute (c'est le cas pour `user1`). Sinon (pour `user2`) l'évaluation s'arrête sans erreur.
La syntaxe `?.[]` Fonctionne également, si nous voulons utiliser des crochets `[]` pour accéder aux propriétés au lieu du point `.`. Similaire aux cas précédents, il permet de lire en toute sécurité une propriété à partir d'un objet qui peut ne pas exister.
```js run
let user1 = {
firstName: "John"
};
let user2 = null; // Imaginez, nous ne pouvons pas autoriser l'utilisateur
let key = "firstName";
alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined
alert( user1?.[key]?.something?.not?.existing); // undefined
```
Nous pouvons également utiliser `?.` avec `delete` :
```js run
delete user?.name; // supprime user.name si user existe
```
```warn header="Nous pouvons utiliser `?.` pour lire et supprimer en toute sécurité, mais pas pour écrire"
Le chaînage optionnel `?.` n'a aucune utilité sur le côté gauche d'une affectation :
For example:
```js run
let user = null;
user?.name = "John"; // Erreur, ne fonctionne pas
// car il évalue à undefined = "John"
```
Ce n'est tout simplement pas si intelligent.
## Résumé
Le chaînage optionnel '?.' A trois formes :
1. `obj?.prop` -- retourne `obj.prop` si `obj` existe, sinon `undefined`.
2. `obj?.[prop]` -- retourne `obj[prop]` si `obj` existe, sinon `undefined`.
3. `obj?.method()` -- appel `obj.method()` si `obj.method` existe, sinon retourne `undefined`.
Comme nous pouvons le voir, tous sont simples et simples à utiliser. Le `?.` vérifie la partie gauche pour `nul/undefined` et permet à l'évaluation de se poursuivre si ce n'est pas le cas.
Une chaîne de `?.` permet d'accéder en toute sécurité aux propriétés imbriquées.
Néanmoins, nous devons appliquer «?.» Avec soin, uniquement s'il est acceptable que la partie gauche n'existe pas. Pour ne pas nous cacher les erreurs de programmation, si elles se produisent.