Les deux structures de données les plus utilisées en JavaScript sont Object et Array.
- Les objets nous permettent de créer une seule entité qui stocke les éléments de données par clé.
- Les tableaux nous permettent de rassembler des éléments de données dans une liste ordonnée.
Mais lorsque nous transmettons ceux-ci Ă une fonction, il se peut que celle-ci nâait pas besoin dâun objet / tableau dans son ensemble, mais plutĂŽt de morceaux individuels.
Lâaffectation par dĂ©composition est une syntaxe spĂ©ciale qui nous permet de âdĂ©compresserâ des tableaux ou des objets dans un ensemble de variables, ce qui est parfois plus pratique.
La décomposition fonctionne également trÚs bien avec des fonctions complexes comportant de nombreux paramÚtres, valeurs par défaut, etc.
DĂ©composition dâun tableau
Voici un exemple de la façon dont un tableau est décomposé en variables :
// nous avons un tableau avec le prénom et le nom
let arr = ["John", "Smith"]
// l'affectation par décomposition
// sets firstName = arr[0]
// and surname = arr[1]
let [firstName, surname] = arr;
alert(firstName); // John
alert(surname); // Smith
Maintenant, nous pouvons travailler avec des variables plutĂŽt que des parties du tableau.
Cela a lâair pas mal quand câest combinĂ© avec split ou tout autre mĂ©thode de renvoi de tableau :
let [firstName, surname] = "John Smith".split(' ');
alert(firstName); // John
alert(surname); // Smith
Comme vous pouvez le voir, la syntaxe est simple. Il y a cependant plusieurs dĂ©tails particuliers. Voyons plus dâexemples, pour mieux le comprendre.
Cette manipulation est appelĂ©e âaffectation par dĂ©compositionâ, car elle âse dĂ©composeâ en copiant ses Ă©lĂ©ments dans des variables. Mais le tableau lui-mĂȘme nâest pas modifiĂ©.
Câest juste une façon plus courte dâĂ©crire :
// let [firstName, surname] = arr;
let firstName = arr[0];
let surname = arr[1];
Les Ă©lĂ©ments indĂ©sirables du tableau peuvent Ă©galement ĂȘtre supprimĂ©s via une virgule supplĂ©mentaire :
// second element is not needed
let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
alert( title ); // Consul
Dans le code ci-dessus, le deuxiĂšme Ă©lĂ©ment du tableau est ignorĂ©, le troisiĂšme est attribuĂ© Ă title et le reste du tableau est Ă©galement ignorĂ© (car il nây a pas de variables pour eux).
âŠEn fait, nous pouvons lâutiliser avec nâimporte quel itĂ©rable, pas seulement les tableaux :
let [a, b, c] = "abc"; // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3]);
That works, because internally a destructuring assignment works by iterating over the right value. Itâs a kind of syntax sugar for calling for..of over the value to the right of = and assigning the values.
Nous pouvons utiliser nâimporte quels âassignablesâ Ă gauche.
Par exemple, une propriĂ©tĂ© dâobjet :
let user = {};
[user.name, user.surname] = "John Smith".split(' ');
alert(user.name); // John
alert(user.surname); // Smith
Dans le chapitre précédent, nous avons vu la méthode Object.entries(obj).
Nous pouvons lâutiliser avec la dĂ©composition pour boucler sur les clĂ©s et valeurs dâun objet :
let user = {
name: "John",
age: 30
};
// boucler sur les clés et les valeurs
for (let [key, value] of Object.entries(user)) {
alert(`${key}:${value}`); // name:John, ensuite age:30
}
Le code similaire pour un Map est plus simple, car il est itérable :
let user = new Map();
user.set("name", "John");
user.set("age", "30");
// Map est itéré sous forme de paires [key, value], ce qui est trÚs pratique pour la déstructuration
for (let [key, value] of user) {
alert(`${key}:${value}`); // name:John, ensuite age:30
}
Il existe une astuce bien connue pour permuter les valeurs de deux variables Ă lâaide dâune affectation de dĂ©structuration :
let guest = "Jane";
let admin = "Pete";
// Permutons les valeurs : guest=Pete, admin=Jane
[guest, admin] = [admin, guest];
alert(`${guest} ${admin}`); // Pete Jane (échangé avec succÚs !)
Ici, nous crĂ©ons un tableau temporaire de deux variables et le dĂ©structurons immĂ©diatement dans lâordre permutĂ©.
Nous pouvons échanger plus de deux variables de cette façon.
Le rest ââŠâ
Habituellement, si le tableau est plus long que la liste Ă gauche, les Ă©lĂ©ments âsupplĂ©mentairesâ sont omis.
Par exemple, ici, seuls deux éléments sont pris et le reste est simplement ignoré :
let [name1, name2] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
alert(name1); // Julius
alert(name2); // Caesar
// Les autres éléments ne sont affectés nulle part
Si nous souhaitons Ă©galement rassembler tout ce qui suit, nous pouvons ajouter un paramĂštre supplĂ©mentaire qui permet dâobtenir âle resteâ Ă lâaide de trois points "..." :
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
// reste est un tableau d'éléments, à partir du 3Úme
alert(rest[0]); // Consul
alert(rest[1]); // of the Roman Republic
alert(rest.length); // 2
La valeur de rest est le tableau des éléments du tableau restants.
Nous pouvons utiliser nâimporte quel autre nom de variable Ă la place de rest, assurez-vous simplement quâil a trois points devant lui et soit placĂ© en dernier dans lâaffectation par dĂ©composition.
let [name1, name2, ...titles] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
// maintenant titles = ["Consul", "of the Roman Republic"]
Les valeurs par défaut
Si le tableau est plus court que la liste des variables Ă gauche, il nây aura aucune erreur. Les valeurs absentes sont considĂ©rĂ©es comme non dĂ©finies :
let [firstName, surname] = [];
alert(firstName); // undefined
alert(surname); // undefined
Si nous voulons quâune valeur âpar dĂ©fautâ remplace la valeur manquante, nous pouvons la fournir en utilisant = :
// les valeurs par défaut
let [name = "Guest", surname = "Anonymous"] = ["Julius"];
alert(name); // Julius (depuis le tableau)
alert(surname); // Anonymous (valeur par défaut)
Les valeurs par dĂ©faut peuvent ĂȘtre des expressions plus complexes ou mĂȘme des appels de fonction. Ils ne sont Ă©valuĂ©s que si la valeur nâest pas fournie.
Par exemple, nous utilisons ici la fonction prompt pour deux valeurs par défaut :
// ne demande que le nom de famille
let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"];
alert(name); // Julius (depuis le tableau)
alert(surname); // saisi par l'utilisateur
Notez que le prompt ne sâexĂ©cutera que pour la valeur manquante (surname).
DĂ©composition dâobject
Lâaffectation par dĂ©composition fonctionne Ă©galement avec les objets.
La syntaxe de base est la suivante :
let {var1, var2} = {var1:âŠ, var2:âŠ}
Nous devrions avoir un objet existant sur le cĂŽtĂ© droit, que nous voulons diviser en variables. La partie gauche contient un âmodĂšleâ de type objet pour les propriĂ©tĂ©s correspondantes. Dans le cas le plus simple, câest une liste de noms de variables dans {...}.
Par exemple :
let options = {
title: "Menu",
width: 100,
height: 200
};
let {title, width, height} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
Les propriétés options.title, options.width et options.height sont affectées aux variables correspondantes.
Lâordre nâa pas dâimportance. Cela fonctionne aussi :
// changé l'ordre dans let {...}
let {height, width, title} = { title: "Menu", height: 200, width: 100 }
Le pattern Ă gauche peut ĂȘtre plus complexe et spĂ©cifier le mapping entre propriĂ©tĂ©s et variables.
Si nous voulons affecter une propriété à une variable portant un autre nom, par exemple, options.width pour aller dans la variable nommée w, alors nous pouvons la définir en utilisant deux points :
let options = {
title: "Menu",
width: 100,
height: 200
};
// { sourceProperty: targetVariable }
let {width: w, height: h, title} = options;
// width -> w
// height -> h
// title -> title
alert(title); // Menu
alert(w); // 100
alert(h); // 200
Les deux points montrent âquoi: va oĂčâ. Dans lâexemple ci-dessus, la propriĂ©tĂ© width est dĂ©finie sur w, la propriĂ©tĂ© height est dĂ©finie sur h et le title est attribuĂ© au mĂȘme nom.
Pour les propriĂ©tĂ©s potentiellement manquantes, nous pouvons dĂ©finir les valeurs par dĂ©faut Ă lâaide de "=", comme ceci :
let options = {
title: "Menu"
};
let {width = 100, height = 200, title} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
Comme pour les tableaux ou les paramĂštres de fonction, les valeurs par dĂ©faut peuvent ĂȘtre des expressions ou mĂȘme des appels de fonction. Elles seront Ă©valuĂ©es si la valeur nâest pas fournie.
Le code prompt ci-dessous demande la width, mais pas le title.
let options = {
title: "Menu"
};
let {width = prompt("width?"), title = prompt("title?")} = options;
alert(title); // Menu
alert(width); // saisi par l'utilisateur
Nous pouvons Ă©galement combiner les deux points et lâĂ©galitĂ© :
let options = {
title: "Menu"
};
let {width: w = 100, height: h = 200, title} = options;
alert(title); // Menu
alert(w); // 100
alert(h); // 200
Si nous avons un objet complexe avec de nombreuses propriétés, nous pouvons extraire que ce dont nous avons besoin :
let options = {
title: "Menu",
width: 100,
height: 200
};
// extraire uniquement le titre en tant que variable
let { title } = options;
alert(title); // Menu
Le pattern rest ââŠâ
Et si lâobjet a plus de propriĂ©tĂ©s que de variables ? Peut-on en prendre puis assigner le ârestâ quelque part ?
Nous pouvons utiliser le modĂšle rest, comme nous lâavons fait avec les tableaux. Il nâest pas pris en charge par certains navigateurs plus anciens (IE, utilisez Babel pour le polyfiller), mais fonctionne avec les modernes.
Cela ressemble Ă ceci :
let options = {
title: "Menu",
height: 200,
width: 100
};
// title = propriété nommée title
// rest = objet avec le reste des propriétés
let {title, ...rest} = options;
// maintenant title="Menu", rest={height: 200, width: 100}
alert(rest.height); // 200
alert(rest.width); // 100
let, ça coinceDans les exemples ci-dessus, les variables ont Ă©tĂ© dĂ©clarĂ©es juste avant lâaffectation : let {âŠ} = {âŠ}. Bien sĂ»r, nous pourrions aussi utiliser des variables existantes. Mais il y a un problĂšme.
Cela ne fonctionnera pas :
let title, width, height;
// erreur dans cette ligne
{title, width, height} = {title: "Menu", width: 200, height: 100};
Le problĂšme est que JavaScript traite {...} dans le flux de code principal (pas dans une autre expression) en tant que bloc de code. De tels blocs de code peuvent ĂȘtre utilisĂ©s pour regrouper des instructions, comme ceci :
{
// un bloc de code
let message = "Hello";
// ...
alert( message );
}
Donc ici, JavaScript suppose que nous avons un bloc de code, câest pourquoi il y a une erreur. Nous voulons plutĂŽt la dĂ©structuration.
Pour montrer Ă JavaScript quâil ne sâagit pas dâun bloc de code, nous pouvons envelopper lâexpression entre parenthĂšses (...) :
let title, width, height;
// maintenant c'est bon
({title, width, height} = {title: "Menu", width: 200, height: 100});
alert( title ); // Menu
Décomposition imbriquée
Si un objet ou un tableau contient dâautres objets et tableaux imbriquĂ©s, nous pouvons utiliser des modĂšles Ă gauche plus complexes pour extraire des parties plus profondes.
Dans le code ci-dessous, options a un autre objet dans la propriĂ©tĂ© size et un tableau dans la propriĂ©tĂ© items. Le modĂšle Ă gauche de lâaffectation a la mĂȘme structure pour en extraire des valeurs :
let options = {
size: {
width: 100,
height: 200
},
items: ["Cake", "Donut"],
extra: true
};
// affectation par décomposition divisée sur plusieurs lignes pour la clarté
let {
size: { // mettre la taille ici
width,
height
},
items: [item1, item2], // attribuer des éléments ici
title = "Menu" // non présent dans l'objet (la valeur par défaut est utilisée)
} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
alert(item1); // Cake
alert(item2); // Donut
Toutes les propriĂ©tĂ©s de lâobjet options, Ă lâexception de extra qui est absente dans la partie gauche, sont affectĂ©s aux variables correspondantes :
Finnalement nous avons width, height, item1, item2 et title à partir de la valeur par défaut.
Notez quâil nây a pas de variables pour size et items, car nous prenons leur contenu Ă la place.
ParamĂštres de fonction intelligente
Il peut arriver quâune fonction ait plusieurs paramĂštres, dont la plupart sont facultatifs. Câest particuliĂšrement vrai pour les interfaces utilisateur. Imaginez une fonction qui crĂ©e un menu. Il peut avoir une largeur, une hauteur, un titre, une liste dâarticles, etc.
Voici une mauvaise façon dâĂ©crire ce genre de fonction :
function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
// ...
}
Dans la vraie vie, le problĂšme est de savoir comment retenir lâordre des arguments. Habituellement, les IDE essaient de nous aider, surtout si le code est bien documentĂ©, mais quand mĂȘme⊠Un autre problĂšme est de savoir comment appeler une fonction lorsque la plupart des paramĂštres sont corrects par dĂ©faut.
Comme ceci ?
// undefined oĂč les valeurs par dĂ©faut sont adĂ©quates
showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])
Câest moche. Et devient illisible lorsque nous traitons plus de paramĂštres.
La décomposition vient à la rescousse !
Nous pouvons passer des paramĂštres sous forme dâobjet, et la fonction les dĂ©composent immĂ©diatement en variables :
// on passe un objet Ă fonction
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
// ...et il est immédiatement étendu aux variables
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
// title, items â pris des options,
// width, height â dĂ©fauts utilisĂ©s
alert( `${title} ${width} ${height}` ); // My Menu 200 100
alert( items ); // Item1, Item2
}
showMenu(options);
Nous pouvons également utiliser une décomposition plus complexe avec des objets imbriqués et des mappings de deux points :
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
function showMenu({
title = "Untitled",
width: w = 100, // width va Ă w
height: h = 200, // height va Ă h
items: [item1, item2] // items premier élément va à item1, deuxiÚme à item2
}) {
alert( `${title} ${w} ${h}` ); // My Menu 100 200
alert( item1 ); // Item1
alert( item2 ); // Item2
}
showMenu(options);
La syntaxe complĂšte est la mĂȘme que pour une affectation par dĂ©composition :
function({
incomingProperty: varName = defaultValue
...
})
Ensuite, pour un objet de paramÚtres, il y aura une variable varName pour la propriété incomingProperty, avec defaultValue par défaut.
Veuillez noter quâune telle dĂ©structuration suppose que showMenu() a un argument. Si nous voulons toutes les valeurs par dĂ©faut, alors nous devrions spĂ©cifier un objet vide :
showMenu({}); // ok, toutes les valeurs par défaut
showMenu(); // cela donnerait une erreur
Nous pouvons rĂ©soudre ce problĂšme en faisant de {} la valeur par dĂ©faut pour lâobjet entier de paramĂštres :
function showMenu({ title = "Menu", width = 100, height = 200 } = {}) {
alert( `${title} ${width} ${height}` );
}
showMenu(); // Menu 100 200
Dans le code ci-dessus, la totalité des arguments objet est {} par défaut, il y a donc toujours quelque chose à décomposer.
Résumé
-
Lâaffectation par dĂ©composition permet de mapper instantanĂ©ment un objet ou un tableau sur de nombreuses variables.
-
La syntaxe complĂšte de lâobjet :
let {prop : varName = default, ...rest} = objectCela signifie que la propriété
propdoit aller dans la variablevarNameet que, si aucune propriĂ©tĂ© de ce type nâexiste, la valeurdefaultdoit ĂȘtre utilisĂ©e.Les propriĂ©tĂ©s dâobjet sans mappage sont copiĂ©es dans lâobjet
rest. -
La syntaxe complĂšte du tableau :
let [item1 = default, item2, ...rest] = arrayLe premier item va Ă
item1; le second passe Ăitem2, tout le reste du tableau correspond aurest. -
Il est possible dâextraire des donnĂ©es de tableaux / objets imbriquĂ©s, pour cela le cĂŽtĂ© gauche doit avoir la mĂȘme structure que le droit.
Commentaires
<code>, pour plusieurs lignes â enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepenâŠ)