L'affectation par décomposition (destructuring en anglais) est une expression JavaScript qui permet d'extraire des données d'un tableau ou d'un objet grâce à une syntaxe dont la forme ressemble à la structure du tableau ou de l'objet.
Syntaxe
[a, b] = [1, 2]; [a, b, ...c] = [1, 2, 3, 4, 5]; {a, b} = {a:1, b:2}; // Syntaxe ES7 - pas implémentée pour Firefox 47a01 ({a, b, ...rest} = {a:1, b:2, c:3, d:4});
Note : {a, b} = {a:1, b:2}
n'est pas syntaxiquement valide en tant que tel, en effet {a, b}
est ici considéré comme un bloc et non comme un littéral objet.
Cependant, ({a, b} = {a:1, b:2})
sera valide comme pour la forme var {a, b} = {a:1, b:2}
.
Description
Ces expressions utilisant des littéraux pour les objets ou les tableaux permettent de créer simplement des données regroupées. Une fois créées, on peut les utiliser de n'importe quelle façon, y compris comme valeur renvoyée par une fonction.
var x = [1, 2, 3, 4, 5]; // On crée un "paquet" de donnée var [y, z] = x; // On utilise l'affectation par décomposition console.log(y); // 1 console.log(z); // 2
L'intérêt de l'assignation par décomposition est de pouvoir lire une structure entière en une seule instruction. Il y a également d'autres choses que vous pouvez faire avec cette expression, comme montré dans les exemples ci-dessous.
Cette syntaxe est semblable aux fonctionnalités offertes par Perl et Python.
Décomposition d'un tableau
Exemple simple
var toto= ["un", "deux", "trois"]; // sans utiliser la décomposition var un = toto[0]; var deux = toto[1]; var trois = toto[2]; // en utilisant la décomposition var [un, deux, trois] = toto;
Affectation sans déclaration
L'affectation par décomposition peut être effectuée sans qu'il y ait de déclaration directement dans l'instruction d'affectation. Par exemple :
var a, b; [a, b] = [1, 2];
Valeurs par défaut
On peut définir une valeur par défaut au cas où la valeur fournie par le tableau soit undefined
. Par exemple :
var a, b; [a = 5, b = 7] = [1]; console.log(a); // 1 console.log(b); // 7
Échange de variables
Une fois le fragment de code exécuté, on aura b égal à 1 et a égal à 3. S'il n'avait pas été possible d'utiliser l'affectation par décomposition, l'échange des valeurs aurait nécessité une variable temporaire (pour des données binaires, on aurait pu utiliser une permutation XOR).
var a = 1; var b = 3; [a, b] = [b, a];
Renvoyer plusieurs valeurs
Grâce à l'affectation par décomposition, les fonctions peuvent renvoyer plusieurs valeurs. Il était déjà possible de renvoyer un tableau mais cela ajoute un nouveau degré de flexibilité.
function f() { return [1, 2]; }
Les valeurs de retour sont déclarées via une syntaxe semblable à celle utilisée pour déclarer les tableaux, utilisant les crochets. On peut ainsi renvoyer autant de valeurs que souhaité. Dans cet exemple, f()
renvoie les valeurs [1, 2]
.
var a, b; [a, b] = f(); console.log("A vaut " + a + " B vaut " + b);
L'instruction [a, b] = f()
assigne, dans l'ordre, les résultats de la fonction aux variables représentées entre les crochets. Ainsi, ici a vaut 1 et b vaut 2.
On peut également récupérer la valeur de retour comme un tableau :
var x = f(); console.log("X vaut " + x);
Et on aura x qui sera égal au tableau contenant 1 et 2.
Ignorer certaines valeurs
On peut également ignorer certaines des valeurs renvoyées qu'on ne souhaiterait pas traiter :
function f() { return [1, 2, 3]; } var [a, , b] = f(); console.log("A vaut " + a + " B vaut " + b);
Après avoir exécuté ce code, on aura a égal à 1 et b égal à 3. La valeur 2 est ignorée. On peut ignorer n'importe laquelle des valeurs (voire toutes). Par exemple :
[,,] = f();
Exploiter les résultats d'une expression rationnelle
Lorsque la méthode exec()
, liées aux expressions rationnelles, trouve une correspondance, elle renvoie un tableau qui contient d'abord la partie complète de la chaîne qui correspond puis ensuite les différentes portions correspondant aux différents groupes. L'affectation par décomposition permet de filtrer simplement les valeurs qu'on souhaite exploiter. Ici, on ignore le premier élément qui est la correspondance complète :
var url = "https://developer.mozilla.org/fr/docs/Web/JavaScript/"; var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url); var [, protocol, fullhost, fullpath] = parsedURL; console.log(protocol); // enregistre "https"
Décomposer un objet
Exemple simple
var o = {p: 42, q: true}; var {p, q} = o; console.log(p); // 42 console.log(q); // true // Assign new variable names var {p: toto, q: truc} = o; console.log(toto); // 42 console.log(truc); // true
Affectation sans déclaration
Il est possible d'effectuer une affectation par décomposition même si aucune déclaration n'est directement utilisée dans l'instruction d'affectation. Par exemple !
var a, b; ({a, b} = {a:1, b:2});
Les parenthèses ( .. )
utilisées autour de l'instruction sont nécessaires pour que la partie gauche soit bien interprétée comme un littéral objet et non comme un bloc.
Arguments par défaut d'une fonction
Version ES5
function dessinGrapheES5(options) { options = options === undefined ? {} : options; var size = options.size === undefined ? 'big' : options.size; var cords = options.cords === undefined ? { x: 0, y: 0 } : options.cords; var radius = options.radius === undefined ? 25 : options.radius; console.log(size, cords, radius); // seulement ensuite on dessine le graphe } dessinGrapheES5({ cords: { x: 18, y: 30 }, radius: 30 });
Version ES6
function dessinGrapheES6({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = {}) { console.log(size, cords, radius); // on dessine le graphe } // Firefox, n'implémente pas encore les valeurs par // défaut pour les affectations par décomposition // Pour contourner le problème, on peut écrire les // paramètres de la façon suivante // ({size: size = 'big', cords: cords = { x: 0, y: 0 }, // radius: radius = 25} = {}) dessinGrapheES6({ cords: { x: 18, y: 30 }, radius: 30 });
Décomposition imbriquée avec objets et tableaux
var metadata = { title: "Scratchpad", translations: [ { locale: "de", localization_tags: [ ], last_edit: "2014-04-14T08:43:37", url: "/de/docs/Tools/Scratchpad", title: "JavaScript-Umgebung" } ], url: "/en-US/docs/Tools/Scratchpad" }; var { title: englishTitle, translations: [{ title: localeTitle }] } = metadata; console.log(englishTitle); // "Scratchpad" console.log(localeTitle); // "JavaScript-Umgebung"
Décomposition et utilisation de for of
var personnes = [ { nom: "Alain Dupont", famille: { mère: "Isabelle Dupont", père: "Jean Dupont", sœur: "Laure Dupont" }, âge: 35 }, { nom: "Luc Marchetoile", famille: { mère: "Patricia Marchetoile", père: "Antonin Marchetoile", frère: "Yann Marchetoile" }, âge: 25 } ]; for (var {nom: n, famille: { père: f } } of personnes) { console.log("Nom : " + n + ", Père : " + f); } // "Nom : Alain Dupont, Père : Jean Dupont" // "Nom : Luc Marchetoile, Père : Antonin Marchetoile"
Interpréter les propriétés d'objets passés en arguments
function userId({id}) { return id; } function whois({displayName: displayName, fullName: {firstName: name}}){ console.log(displayName + " est " + name); } var user = { id: 42, displayName: "jbiche", fullName: { firstName: "Jean", lastName: "Biche" } }; console.log("userId: " + userId(user)); // "userId: 42" whois(user); // "jbiche est Jean"
Cela permet d'accéder directement à id
, displayName
et firstName
depuis l'objet user
.
Les noms de propriétés calculés et la décomposition
Il est possible d'utiliser des noms de propriétés calculés, comme avec les littéraux objets, avec la décomposition.
let clé = "z"; let { [clé]: toto } = { z: "truc" }; console.log(toto); // "truc"
Spécifications
Spécification | État | Commentaires |
---|---|---|
ECMAScript 2015 (6th Edition, ECMA-262) La définition de 'Destructuring assignment' dans cette spécification. |
Standard | Définition initiale. |
ECMAScript 2017 Draft (ECMA-262) La définition de 'Destructuring assignment' dans cette spécification. |
Projet |
Compatibilité des navigateurs
Fonctionnalité | Chrome | Firefox (Gecko) | Edge | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|---|
Support simple | 49.0 | 2.0 (1.8.1) | 14 | Pas de support | Pas de support | 7.1 |
Noms de propriétés calculés | 49.0 | 34 (34) | 14 | Pas de support | Pas de support | Pas de support |
Opérateur de décomposition | 49.0 | 34 (34) | 12[1] | ? | ? | ? |
Fonctionnalité | Android | Chrome pour Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|
Support simple | Pas de support | 49.0 | 1.0 (1.0) | Pas de support | Pas de support | 8 |
Noms de propriétés calculés | Pas de support | 49.0 | 34.0 (34) | Pas de support | Pas de support | Pas de support |
Opérateur de décomposition | ? | 49.0 | 34.0 (34) | ? | ? | ? |
[1] Dans about:flags
il faut activer « Activer les fonctionnalités JavaScript expérimentales ».
Notes spécifiques à Firefox
- Firefox fournissait une extension non-standard du langage pour la décomposition avec JS1.7. Cette extension a été retirée avec Gecko 40 (Firefox 40 / Thunderbird 40 / SeaMonkey 2.37). See bug 1083498.
- À partir de Gecko 41 (Firefox 41 / Thunderbird 41 / SeaMonkey 2.38) et afin de se conformer à la spécification ES6, les motifs de décompositions entre parenthèses tels que
([a, b]) = [1, 2]
ou({a, b}) = { a: 1, b: 2 }
sont désormais considérés invalides et lèveront une exceptionSyntaxError
. Voir le billet de Jeff Walden à ce sujet et le bug 1146136 pour plus de détails.