Principes fondamentaux et syntaxe
L'opérateur de décomposition, identifié par trois points consécutifs (...), fonctionne comme l'inverse du paramètre de reste (rest). Il permet d'extraire les éléments d'un objet itérable pour les injecter sous forme de liste d'arguments séparés par des virgules.
const palette = ['cyan', 'magenta', 'jaune'];
console.log(...palette);
// Affiche : cyan magenta jaune
console.log('début', ...palette, 'fin');
// Affiche : début cyan magenta jaune fin
Cette syntaxe est particulièrement pratique lors de l'appel de fonctions, car elle évite la manipulation manuelle des paramètres. Elle accepte également des expressions conditionnelles en aval.
const fusionnerListes = (conteneur, ...elements) => {
const resultat = [...conteneur];
resultat.push(...elements);
return resultat;
};
const configuration = [true, 42];
fusionnerListes([], ...configuration); // Retourne [true, 42]
// Intégration d'une expression ternaire
const listeDefinitive = [
...(isActif ? ['admin', 'moderateur'] : []),
'lecteur'
];
Si l'opérateur est appliqué à un tableau vide, il ne génère aucune insertion dans la structure cible.
const combinaison = [...[], 'unique']; // ['unique']
Supplantation de la méthode apply()
Dans les versions antérieures d'ECMAScript, Function.prototype.apply() était indispensable pour trasnmettre un tableau en tant qu'arguments dispersés. La décomposition rend désormais cette approche obsolète.
// Ancienne pratique (ES5)
function calculerTotal(x, y, z) { /* ... */ }
const donnees = [10, 20, 30];
calculerTotal.apply(null, donnees);
// Pratique moderne (ES6+)
calculerTotal(...donnees);
Exemple concret : extraction de la valeur minimale dans une collection numérique.
const mesures = [85, 12, 40];
// Remplace Math.min.apply(null, mesures)
const minimum = Math.min(...mesures); // 12
Clonage et fusion de tableaux
L'affectation directe d'un tableau à une nouvelle varible ne crée pas de copie indépendante, mais partage simplement la référence mémoire. Toute modification sur la nouvelle varible altère immédiatement l'original.
const source = [10, 20];
const reference = source;
reference[1] = 99;
console.log(source); // [10, 99]
Pour obtenir une réplique autonome, l'opérateur de décomposition offre une syntaxe épurée :
const original = [5, 15];
const copie = [...original];
// Équivalent via la déstructuration
const [...duplication] = original;
Pour assembler plusieurs collections, la syntaxe remplace avantageusement la méthode concat() :
const groupeA = ['alpha', 'beta'];
const groupeB = ['gamma'];
const groupeC = ['delta', 'epsilon'];
const fusion = [...groupeA, ...groupeB, ...groupeC];
// ['alpha', 'beta', 'gamma', 'delta', 'epsilon']
Il est essentiel de noter que cette opération réalise une copie superficielle (shallow copy). Les objets imbriqués restent partagés par référence.
const tab1 = [{ id: 1 }];
const tab2 = [{ id: 2 }];
const joint = [...tab1, ...tab2];
joint[0] === tab1[0]; // true, la référence mémoire est conservée
Lors d'une affectation par déstructuration, l'opérateur doit impérativement occuper la dernière position. Tout autre placement déclenche une erreur de syntaxe.
// Syntaxe invalide
const [...partieFinale, dernier] = [1, 2, 3];
const [premier, ...milieu, dernier] = [1, 2, 3];
Manipulation de chaînes et caractères Unicode
L'opérateur transforme une chaîne de caractères en un tableau de ses unités constitutives. Son avantage principal réside dans la gestion correcte des caractères UTF-16 sur 4 octets (notamment les emojis), contrairement à la propriété length classique qui compte les unités de code.
const texte = 'bonjour';
[...texte]; // ['b', 'o', 'n', 'j', 'o', 'u', 'r']
const emojiSequence = '🚀🎉';
// La propriété length compte des paires de substitution, donc 4
console.log(emojiSequence.length); // 4
// La décomposition reconnaît les points de code complets, donc 2
console.log([...emojiSequence].length); // 2
Itérables personnalisés : Maps, Sets et Generators
Toute structure implémentant le protocole d'itération peut être décomposée sans effort supplémentaire.
const correspondances = new Map([
['identifiant1', 'valeurA'],
['identifiant2', 'valeurB']
]);
const extraireClefs = [...correspondances.keys()]; // ['identifiant1', 'identifiant2']
Les fonctions génératrices retournent un itérateur. Elles s'intègrent naturellement avec cette syntaxe pour matérialiser une séquence de valeurs :
function* generateurTeintes() {
yield 'rouge';
yield 'bleu';
yield 'vert';
}
[...generateurTeintes()]; // ['rouge', 'bleu', 'vert']
En revanche, tenter d'appliquer l'opérateur sur une structure non itérable déclenche systématiquement une exception TypeError.
const metrique = { valeur: 100 };
const tableau = [...metrique]; // Erreur : l'objet n'implémente pas Symbol.iterator