10. Déstructuration
10. Déstructuration #
- 10.1. Aperçu
- 10.1.1. Déstructuration d’objets
- 10.1.2. Déstructuration de tableau
- 10.1.3. Où la déstructuration peut-elle être utilisée ?
- 10.2. Contexte: Construire des données par rapport à extraire des données
- 10.3. Modèles de déstructuration
- 10.3.1. Choisissez ce dont vous avez besoin
- 10.4. Comment les modèles accèdent-ils aux entrailles des valeurs ?
- 10.4.1. Les modèles d’objets contraignent les valeurs aux objets
- 10.4.2. Les modèles de tableau fonctionnent avec des itérables
- 10.5. Valeurs par défaut
- 10.5.1.
undefined
déclenche les valeurs par défaut - 10.5.2. Les valeurs par défaut sont calculées à la demande
- 10.5.3. Les valeurs par défaut peuvent faire référence à d’autres variables dans le modèle
- 10.5.4. Valeurs par défaut pour les motifs
- 10.5.5. Valeurs par défaut plus complexes
- 10.5.1.
- 10.6. Plus de fonctionnalités de déstructuration d’objets
- 10.6.1. Valeur de la propriété raccourcis
- 10.6.2. Clés de propriété calculées
- 10.7. Plus de fonctionnalités de déstructuration de tableaux
- 10.7.1. Élision
- 10.7.2. Opérateur de repos (
...
)
- 10.8. Vous pouvez affecter à plus que de simples variables
- 10.9. Pièges de la déstructuration
- 10.9.1. Ne commencez pas une déclaration avec une accolade
- 10.10. Exemples de déstructuration
- 10.10.1. La déstructuration a renvoyé des tableaux
- 10.10.2. Déstructuration des objets renvoyés
- 10.10.3. Tableau – déstructuration des valeurs itérables
- 10.10.4. Valeurs de retour multiples
- 10.11. L’algorithme de déstructuration
- 10.11.1. L’algorithme
- 10.11.2. Application de l’algorithme
10.1 Aperçu #
La déstructuration est un moyen pratique d’extraire plusieurs valeurs à partir de données stockées dans des objets et des tableaux (éventuellement imbriqués). Il peut être utilisé dans des emplacements qui reçoivent des données (comme le côté gauche d’une affectation). La façon d’extraire les valeurs est spécifiée via des modèles (lisez la suite pour des exemples).
10.1.1 Déstructuration d’objets #
Déstructuration d’objets:
La déstructuration aide au traitement des valeurs de retour:
10.1.2 Déstructuration de tableau #
Déstructuration de tableau (fonctionne pour toutes les valeurs itérables):
La déstructuration aide au traitement des valeurs de retour:
10.1.3 Où la déstructuration peut-elle être utilisée ? #
La déstructuration peut être utilisée aux emplacements suivants (je montre des modèles de tableau à démontrer; les modèles d’objets fonctionnent tout aussi bien):
Vous pouvez également vous déstructurer dans une boucle for-of
:
10.2 Contexte: Construire des données par rapport à extraire des données #
Pour bien comprendre ce qu’est la déstructuration, examinons d’abord son contexte plus large.
JavaScript a des opérations pour construire des données, une propriété à la fois:
La même syntaxe peut être utilisée pour extraire des données. Encore une fois, une propriété à la fois:
De plus, il existe une syntaxe pour construire plusieurs propriétés en même temps, via un littéral d’objet:
Avant ES6, il n’existait aucun mécanisme correspondant d’extraction de données. C’est ce qu’est la déstructuration – elle vous permet d’extraire plusieurs propriétés d’un objet via un modèle d’objet. Par exemple, sur le côté gauche d’une tâche:
Vous pouvez également déstructurer des tableaux via des motifs:
10.3 Modèles de déstructuration #
Les deux parties suivantes sont impliquées dans la déstructuration:
- Source de déstructuration : les données à déstructurer. Par exemple, le côté droit d’une affectation de déstructuration.
- Cible de déstructuration : le motif utilisé pour la déstructuration. Par exemple, le côté gauche d’une affectation de déstructuration.
La cible de déstructuration est l’un des trois modèles suivants:
- Cible d’affectation. Par exemple:
x
- Une cible d’affectation est généralement une variable. Mais dans l’affectation de déstructuration, vous avez plus d’options, comme je l’expliquerai plus tard.
- Modèle d’objet. Par exemple:
{ first: "pattern", last: "pattern" }
- Les parties d’un modèle d’objet sont des propriétés, les valeurs de propriété sont à nouveau des modèles (récursivement).
- Motif de tableau. Par exemple:
- Les parties d’un motif de tableau sont des éléments, les éléments sont à nouveau des motifs (récursivement).
Cela signifie que vous pouvez imbriquer des motifs, arbitrairement profondément:
10.3.1 Choisissez ce dont vous avez besoin #
Si vous déstructurez un objet, vous ne mentionnez que les propriétés qui vous intéressent:
Si vous déstructurez un tableau, vous pouvez choisir d’extraire uniquement un préfixe:
10.4 Comment les modèles accèdent-ils aux entrailles des valeurs ? #
Dans une affectation pattern = someValue
, comment le pattern
accède-t-il à ce qui se trouve à l’intérieur de someValue
?
10.4.1 Les modèles d’objets contraignent les valeurs aux objets #
Le modèle d’objet contraint les sources de déstructuration aux objets avant d’accéder aux propriétés. Cela signifie que cela fonctionne avec des valeurs primitives:
10.4.1.1 A défaut de déstructurer une valeur #
La coercition à l’objet n’est pas effectuée via Object()
, mais via l’opération interne ToObject()
. Les deux opérations traitent undefined
et null
différemment.
Object()
convertit les valeurs primitives en objets wrapper et laisse les objets intacts:
It also converts undefined
and null
to empty objects:
In contrast, ToObject()
throws a TypeError
if it encounters undefined
or null
. Therefore, the following destructurings fail, even before destructuring accesses any properties:
Par conséquent, vous pouvez utiliser le modèle d’objet vide {}
pour vérifier si une valeur est coercible pour un objet. Comme nous l’avons vu, seuls undefined
et null
ne le sont pas:
Les parenthèses autour des expressions sont nécessaires car les instructions ne doivent pas commencer par des accolades en JavaScript (les détails sont expliqués plus loin).
10.4.2 Les modèles de tableau fonctionnent avec des itérables #
La déstructuration de tableau utilise un itérateur pour accéder aux éléments d’une source. Par conséquent, vous pouvez déstructurer toute valeur itérable. Regardons des exemples de valeurs itérables.
Les chaînes sont itérables:
N’oubliez pas que l’itérateur sur les chaînes renvoie des points de code (« Caractères Unicode », 21 bits), pas des unités de code (« caractères JavaScript », 16 bits). (Pour plus d’informations sur Unicode, consultez le chapitre « Chapitre 24. Unicode et JavaScript « dans « Parler JavaScript ».) Par exemple:
Vous ne pouvez pas accéder aux éléments d’un ensemble via des indices, mais vous pouvez le faire via un itérateur. Par conséquent, la déstructuration des tableaux fonctionne pour les ensembles:
L’itérateur Set
renvoie toujours les éléments dans l’ordre dans lequel ils ont été insérés, c’est pourquoi le résultat de la déstructuration précédente est toujours le même.
10.4.2.1 À défaut de déstructurer un tableau une valeur #
Une valeur est itérable si elle a une méthode dont la clé est Symbol.iterator
qui renvoie un objet. Array-destructuring lève un TypeError
si la valeur à déstructurer n’est pas itérable:
Le TypeError
est lancé avant même d’accéder aux éléments de l’itérable, ce qui signifie que vous pouvez utiliser le modèle de tableau vide pour vérifier si une valeur est itérable:
10.5 Valeurs par défaut #
Les valeurs par défaut sont une fonctionnalité facultative des modèles. Ils fournissent une solution de secours si rien n’est trouvé dans la source. Si une partie (une propriété d’objet ou un élément de tableau) n’a pas de correspondance dans la source, elle est mise en correspondance avec:
- sa valeur par défaut (si elle est spécifiée; c’est facultatif)
-
undefined
( sinon)
Regardons un exemple. Dans la déstructuration suivante, l’élément à l’indice 0 n’a pas de correspondance du côté droit. Par conséquent, la déstructuration continue en faisant correspondre x
à 3, ce qui conduit à ce que x
soit défini sur 3.
Vous pouvez également utiliser des valeurs par défaut dans les modèles d’objets:
10.5.1 undefined
déclencheurs valeurs par défaut #
Les valeurs par défaut sont également utilisées si une pièce a une correspondance et que cette correspondance est undefined
:
La justification de ce comportement est expliquée dans le chapitre suivant, dans la section sur les valeurs par défaut des paramètres.
10.5.2 Les valeurs par défaut sont calculées à la demande #
Les valeurs par défaut elles-mêmes ne sont calculées que lorsqu’elles sont nécessaires. En d’autres termes, cette déstructuration:
est équivalent à:
Vous pouvez observer que si vous utilisez console.log()
:
In the second destructuring, the default value is not triggered and log()
is not called.
10.5.3 Default values can refer to other variables in the pattern #
A default value can refer to any variable, including other variables in the same pattern:
Cependant, la commande compte: les variables x
et y
sont déclarées de gauche à droite et produisent un ReferenceError
si elles sont accessibles avant leurs déclarations:
10.5.4 Valeurs par défaut pour les modèles #
Jusqu’à présent, nous n’avons vu que des valeurs par défaut pour les variables, mais vous pouvez également les associer à des modèles:
Qu’est-ce que cela signifie? Rappelez la règle des valeurs par défaut : Si une pièce n’a pas de correspondance dans la source, la déstructuration continue avec la valeur par défaut.
L’élément à l’indice 0 n’a pas de correspondance, c’est pourquoi la déstructuration continue avec:
Vous pouvez plus facilement voir pourquoi les choses fonctionnent de cette façon si vous remplacez le motif { prop: x }
par la variable pattern
:
10.5.5 Valeurs par défaut plus complexes #
Explorons davantage les valeurs par défaut des modèles. Dans l’exemple suivant, nous attribuons une valeur à x
via la valeur par défaut { prop: 123 }
:
Comme l’élément de tableau à l’index 0 n’a pas de correspondance sur le côté droit, la déstructuration se poursuit comme suit et x
est défini sur 123.
Cependant, x
ne reçoit pas de valeur de cette manière si le côté droit a un élément à l’index 0, car la valeur par défaut n’est pas déclenchée.
Dans ce cas, la déstructuration se poursuit avec:
Ainsi, si vous voulez que x
soit 123 si l’objet ou la propriété est manquant, vous devez spécifier une valeur par défaut pour x
lui-même:
Ici, la déstructuration se poursuit comme suit, indépendamment du fait que le côté droit soit ou
.
10.6 Plus de fonctionnalités de déstructuration d’objets #
10.6.1 Abréviations de valeur de propriété #
Les abréviations de valeur de propriété sont une caractéristique des littéraux d’objets: Si la valeur de la propriété est une variable portant le même nom que la clé de propriété, vous pouvez l’omettre. Cela fonctionne aussi pour la déstructuration:
Vous pouvez également combiner des raccourcis de valeur de propriété avec des valeurs par défaut:
10.6.2 Clés de propriété calculées #
Les clés de propriété calculées sont une autre fonctionnalité littérale d’objet qui fonctionne également pour la déstructuration. Vous pouvez spécifier la clé d’une propriété via une expression, si vous la mettez entre crochets:
Les clés de propriété calculées vous permettent de déstructurer les propriétés dont les clés sont des symboles:
10.7 Plus de fonctionnalités de déstructuration de tableaux #
10.7.1 Elision #
Elision vous permet d’utiliser la syntaxe des « trous » de tableaux pour ignorer les éléments pendant la déstructuration:
10.7.2 Opérateur de repos (...
) #
L’opérateur rest vous permet d’extraire les éléments restants d’un itérable dans un tableau. Si cet opérateur est utilisé dans un modèle de tableau, il doit venir en dernier:
Si l’opérateur ne trouve aucun élément, il fait correspondre son opérande au tableau vide. Autrement dit, il ne produit jamais undefined
ou null
. Par exemple:
L’opérande de l’opérateur rest n’a pas besoin d’être une variable, vous pouvez également utiliser des modèles:
L’opérateur rest déclenche la déstructuration suivante:
10.8 Vous pouvez affecter à plus que de simples variables #
Si vous affectez via la déstructuration, chaque cible d’affectation peut être tout ce qui est autorisé sur le côté gauche d’une affectation normale.
Par exemple, une référence à une propriété (obj.prop
):
Ou une référence à un élément de tableau (arr
):
Vous pouvez également attribuer à des propriétés d’objet et des éléments de tableau via l’opérateur rest (...
):
Si vous déclarez des variables ou définissez des paramètres via la déstructuration, vous devez utiliser des identifiants simples, vous ne pouvez pas faire référence aux propriétés des objets et aux éléments de tableau.
10.9 Pièges de la déstructuration #
Il y a deux choses à prendre en compte lors de l’utilisation de la déstructuration:
- Vous ne pouvez pas commencer une déclaration avec une accolade.
- Pendant la déstructuration, vous pouvez soit déclarer des variables, soit les affecter, mais pas les deux.
Les deux sections suivantes contiennent les détails.
10.9.1 Ne commencez pas une instruction par une accolade #
Car les blocs de code commencent par une accolade, les instructions ne doivent pas commencer par une accolade. Ceci est regrettable lors de l’utilisation de la déstructuration d’objets dans une affectation:
La solution consiste à mettre l’expression complète entre parenthèses:
La syntaxe suivante ne fonctionne pas:
Avec let
, var
et const
, les accolades ne posent jamais de problèmes:
10.10 Exemples de déstructuration #
Commençons par quelques exemples plus petits.
La boucle for-of
prend en charge la déstructuration:
Vous pouvez utiliser la déstructuration pour échanger des valeurs. C’est quelque chose que les moteurs pourraient optimiser, de sorte qu’aucun tableau ne serait créé.
Vous pouvez utiliser la déstructuration pour diviser un tableau:
10.10.1 Déstructuration des tableaux renvoyés #
Certaines opérations JavaScript intégrées renvoient des tableaux. La déstructuration aide à les traiter:
Si vous n’êtes intéressé que par les groupes (et non par la correspondance complète, all
), vous pouvez utiliser elision pour ignorer l’élément de tableau à l’index 0:
exec()
renvoie null
si l’expression régulière ne correspond pas. Malheureusement, vous ne pouvez pas gérer null
via les valeurs par défaut, c’est pourquoi vous devez utiliser l’opérateur Or (||
) dans ce cas:
Array.prototype.split()
renvoie un tableau. Par conséquent, la déstructuration est utile si vous êtes intéressé par les éléments, pas par le tableau:
10.10.2 Déstructuration des objets renvoyés #
La déstructuration est également utile pour extraire des données d’objets renvoyés par des fonctions ou des méthodes. Par exemple, la méthode itérateur next()
renvoie un objet avec deux propriétés, done
et value
. Le code suivant enregistre tous les éléments du tableau arr
via l’itérateur iter
. La déstructuration est utilisée dans la ligne A.
10.10.3 Array-déstructuration des valeurs itérables #
Array-déstructuration fonctionne avec n’importe quelle valeur itérable. Cela est parfois utile:
10.10.4 Valeurs de retour multiples #
Pour voir l’utilité de plusieurs valeurs de retour, implémentons une fonction findElement(a, p)
qui recherche le premier élément du tableau a
pour lequel la fonction p
renvoie true
. La question est: que devrait retourner findElement()
? Parfois, on s’intéresse à l’élément lui-même, parfois à son index, parfois aux deux. L’implémentation suivante renvoie les deux.
La fonction itère sur tous les éléments de array
, via la méthode Tableau entries()
, qui renvoie une itérable sur paires (ligne A). Les parties des paires sont accessibles via la déstructuration.
Utilisons findElement()
:
Plusieurs fonctionnalités d’ECMAScript 6 nous ont permis d’écrire du code plus concis: Le rappel est une fonction de flèche; la valeur de retour est déstructurée via un modèle d’objet avec des raccourcis de valeur de propriété.
En raison de index
et element
se référant également aux clés de propriété, l’ordre dans lequel nous les mentionnons n’a pas d’importance. Nous pouvons les échanger et rien ne change:
Nous avons géré avec succès le cas d’avoir besoin à la fois d’index et d’élément. Et si nous ne nous intéressions qu’à l’un d’entre eux? Il s’avère que, grâce à ECMAScript 6, notre implémentation peut également s’en occuper. Et la surcharge syntaxique par rapport aux fonctions avec des valeurs de retour uniques est minime.
À chaque fois, nous n’extrayons que la valeur de la propriété dont nous avons besoin.
10.11 L’algorithme de déstructuration #
Cette section examine la déstructuration sous un angle différent : en tant qu’algorithme de correspondance de motif récursif.
À la fin, j’utiliserai l’algorithme pour expliquer la différence entre les deux déclarations de fonction suivantes.
10.11.1 L’algorithme #
Une affectation de déstructuration ressemble à ceci:
Nous voulons utiliser pattern
pour extraire des données de value
. Je vais maintenant décrire un algorithme pour le faire, qui est connu en programmation fonctionnelle sous le nom de pattern matching (court: matching). L’algorithme spécifie l’opérateur ←
(« match against ») pour l’affectation de déstructuration qui fait correspondre a pattern
à a value
et attribue des variables tout en le faisant:
L’algorithme est spécifié via des règles récursives qui séparent les deux opérandes de l’opérateur ←
. La notation déclarative peut prendre un peu de temps pour s’y habituer, mais elle rend la spécification de l’algorithme plus concise. Chaque règle comporte deux parties:
- L’en-tête (première ligne) décrit la condition qui déclenche la règle.
- Le corps (lignes restantes) décrit ce qui se passe si la règle est déclenchée.
Regardons un exemple:
- ( 2c)
{key: "pattern", "properties"} ← obj
- ( 2e)
{} ← obj
(il ne reste plus de propriétés)
Dans la règle (2c), la tête signifie que cette règle est exécutée s’il existe un modèle d’objet avec au moins une propriété et zéro ou plusieurs propriétés restantes. Ce modèle est mis en correspondance avec une valeur obj
. L’effet de cette règle est que l’exécution se poursuit avec le modèle de valeur de propriété correspondant à obj.key
et les propriétés restantes correspondant à obj
.
Dans la règle (2e), la tête signifie que cette règle est exécutée si le motif d’objet vide {}
est mis en correspondance avec une valeur obj
. Alors il n’y a rien à faire.
Chaque fois que l’algorithme est appelé, les règles sont vérifiées de haut en bas et seule la première règle applicable est exécutée.
Je montre uniquement l’algorithme d’affectation de déstructuration. Les déclarations de variables de déstructuration et les définitions de paramètres de déstructuration fonctionnent de la même manière.
Je ne couvre pas les fonctionnalités avancées (clés de propriété calculées; raccourcis de valeur de propriété; propriétés d’objet et éléments de tableau en tant que cibles d’affectation), soit. Seulement les bases.
10.11.1.1 Patterns #
Un pattern est soit:
- Une variable:
x
- Un motif d’objet:
{"properties"}
- Un motif de tableau:
Chacune des sections suivantes décrit l’un de ces trois cas.
Les trois sections suivantes précisent comment gérer ces trois cas. Chaque section contient une ou plusieurs règles numérotées.
10.11.1.2 Variable #
- (1)
x ← value
( y comprisundefined
etnull
)
10.11.1.3 Modèle d’objet #
- ( 2a)
{"properties"} ← undefined
- ( 2b)
{"properties"} ← null
- ( 2c)
{key: "pattern", "properties"} ← obj
- ( 2d)
{key: "pattern" = default_value, "properties"} ← obj
- ( 2e)
{} ← obj
(il ne reste plus de propriétés)
10.11.1.4 Modèle de tableau #
Modèle de tableau et itérable. L’algorithme de déstructuration de tableau commence par un modèle de tableau et un itérable:
- ( 3 bis)
← non_iterable
assert(!isIterable(non_iterable))
- ( 3b)
← iterable
assert(isIterable(iterable))
Fonction d’aide:
Éléments de tableau et itérateur. L’algorithme continue avec les éléments du motif (côté gauche de la flèche) et l’itérateur obtenu à partir de l’itérable (côté droit de la flèche).
- ( 3c)
"pattern", "elements" ← iterator
- ( 3d)
"pattern" = default_value, "elements" ← iterator
- ( 3e)
, "elements" ← iterator
(trou, élision)
- ( 3f)
..."pattern" ← iterator
(toujours la dernière partie!)
- ( 3g)
← iterator
(il ne reste plus aucun élément)
Fonction d’aide:
10.11.2 En appliquant l’algorithme #
Dans ECMAScript 6, vous pouvez simuler des paramètres nommés si l’appelant utilise un littéral d’objet et que l’appelé utilise la déstructuration. Cette simulation est expliquée en détail dans le chapitre sur la gestion des paramètres. Le code suivant montre un exemple : la fonction move1()
a deux paramètres nommés, x
et y
:
Il y a trois valeurs par défaut à la ligne A:
- Les deux premières valeurs par défaut vous permettent d’omettre
x
ety
. - La troisième valeur par défaut vous permet d’appeler
move1()
sans paramètres (comme dans la dernière ligne).
Mais pourquoi définiriez-vous les paramètres comme dans l’extrait de code précédent? Pourquoi pas comme suit – qui est également tout à fait légal code ES6?
Pour voir pourquoi move1()
est correct, utilisons les deux fonctions pour deux exemples. Avant de faire cela, voyons comment le passage des paramètres peut être expliqué via la correspondance.
10.11.2.1 Contexte: passage des paramètres via la correspondance #
Pour les appels de fonction, les paramètres formels (définitions de fonction internes) sont mis en correspondance avec les paramètres réels (appels de fonction internes). À titre d’exemple, prenez la définition de fonction suivante et l’appel de fonction suivant.
Les paramètres a
et b
sont configurés de manière similaire à la déstructuration suivante.
10.11.2.2 En utilisant move2()
#
Examinons comment fonctionne la déstructuration pour move2()
.
Exemple 1. move2()
conduit à cette déstructuration:
L’élément de tableau unique sur le côté gauche n’a pas de correspondance sur le côté droit, c’est pourquoi {x,y}
est mis en correspondance avec la valeur par défaut et non avec les données du côté droit (règles 3b, 3d):
Le côté gauche contient des abréviations de valeur de propriété, c’est une abréviation pour:
Cette déstructuration conduit aux deux affectations suivantes (règles 2c, 1):
Exemple 2. Examinons l’appel de fonction move2({z:3})
qui conduit à la déstructuration suivante:
Il y a un élément de tableau à l’index 0 sur le côté droit. Par conséquent, la valeur par défaut est ignorée et l’étape suivante est (règle 3d):
Cela conduit à la fois x
et y
à undefined
, ce qui n’est pas ce que nous voulons.
10.11.2.3 Utilisation move1()
#
Essayons move1()
.
Exemple 1: move1()
Nous n’avons pas d’élément de tableau à l’index 0 sur le côté droit et utilisons la valeur par défaut (règle 3d):
Le côté gauche contient des raccourcis de valeur de propriété, ce qui signifie que cette déstructuration est équivalente à:
Ni la propriété x
ni la propriété y
n’ont de correspondance sur le côté droit. Par conséquent, les valeurs par défaut sont utilisées et les destructurations suivantes sont effectuées ensuite (règle 2d):
Cela conduit aux affectations suivantes (règle 1):
Exemple 2: move1({z:3})
Le premier élément du modèle de tableau a une correspondance sur le côté droit et cette correspondance est utilisée pour continuer la déstructuration (règle 3d):
Comme dans l’exemple 1, il n’y a pas de propriétés x
et y
sur le côté droit et les valeurs par défaut sont utilisées:
10.11.2.4 Conclusion #
Les exemples démontrent que les valeurs par défaut sont une caractéristique des parties de motif (propriétés d’objet ou éléments de tableau). Si une pièce n’a pas de correspondance ou est mise en correspondance avec undefined
, la valeur par défaut est utilisée. C’est-à-dire que le modèle est comparé à la valeur par défaut.