Deux collections qui vont plus loin que Object et Array
Les objets simples et les tableaux couvrent l'essentiel de ce dont on a besoin en JavaScript, mais ils n'ont pas été pensés pour tous les cas de figure. Map et Set sont deux collections natives qui viennent combler deux besoins précis : faire des recherches par clé quand les clés ne sont pas des chaînes de caractères, et gérer des ensembles sans doublons avec une vérification d'appartenance rapide.
Elles font partie du langage depuis ES2015. Les deux sont itérables, exposent une propriété .size, et se marient très bien avec l'opérateur de décomposition. Le modèle mental à retenir est simple :
Map— comme un objet, sauf que les clés peuvent être de n'importe quel type et que l'ordre d'insertion est conservé.Set— comme un tableau, sauf que les valeurs y sont uniques et que la recherche est rapide.
Créer et utiliser une Map en JavaScript
Une Map stocke des paires clé/valeur. On en crée une avec new Map(), puis on utilise .set(), .get(), .has() et .delete() :
Tu peux aussi passer au constructeur un tableau de paires [clé, valeur] pour l'initialiser :
Cette structure en tableau à deux éléments revient partout dès qu'on touche aux Maps — c'est comme ça que les entrées sont représentées pendant l'itération.
Map ou objet : quelle différence en JavaScript ?
À première vue, un simple objet fait exactement le même boulot. Et la plupart du temps, c'est vrai. Mais la Map corrige quelques petits défauts bien précis :
Les objets héritent de Object.prototype, donc des clés comme toString, constructor ou hasOwnProperty existent déjà sur n'importe quel objet. Les Maps n'ont pas ce bagage : les seules clés présentes sont celles que vous ajoutez.
Les autres différences à retenir :
- Tous les types de clés sont acceptés. Une Map accepte les objets, fonctions, nombres et booléens comme clés. Un objet, lui, convertit silencieusement les clés non-string en chaînes :
obj[1]etobj["1"]pointent vers la même entrée. - Ordre d'insertion garanti. Une Map itère dans l'ordre où les entrées ont été ajoutées. Les objets aussi, la plupart du temps, sauf que les clés qui ressemblent à des nombres sont triées en premier — un piège sournois.
- Une taille native.
map.sizeest en O(1). Pour un objet, il faut écrireObject.keys(obj).length, ce qui reconstruit un tableau à chaque fois. - Optimisée pour les modifications fréquentes. Les moteurs JavaScript optimisent les Maps pour les ajouts/suppressions répétés. Les objets, eux, sont optimisés pour des structures stables.
Utilisez un objet quand vous modélisez un enregistrement avec des clés string connues à l'avance ({ name, email, age }). Utilisez une Map quand les clés sont dynamiques, qu'elles ne sont pas des chaînes, ou que vous allez beaucoup ajouter et retirer d'entrées.
Parcourir une Map en JavaScript
Les Maps sont itérables, ce qui veut dire qu'on peut utiliser for...of directement et déstructurer chaque entrée sans effort :
Si tu veux uniquement les clés ou uniquement les valeurs, utilise .keys() ou .values(). Et si tu préfères, .forEach() fait aussi le boulot :
Pour reconvertir une Map en objet classique ou en tableau, il suffit d'utiliser la décomposition :
Créer et utiliser un Set
Un Set stocke des valeurs uniques. Ajouter une valeur qui y figure déjà ne change rien :
L'unicité repose sur la même règle d'égalité que ===, avec une particularité : au sein d'un Set, NaN est considéré comme égal à lui-même, alors que NaN === NaN renvoie false partout ailleurs.
On peut passer un itérable au constructeur pour initialiser un Set — et c'est précisément de là que vient l'astuce pour supprimer les doublons d'un tableau :
Une ligne, n'importe quel type primitif. Ça ne fonctionne pas pour les tableaux d'objets — deux objets différents avec les mêmes champs restent deux valeurs distinctes — mais pour les chaînes, les nombres et les booléens, c'est la façon idiomatique de supprimer les doublons d'un tableau en JavaScript.
Set ou Array : quand basculer ?
Les tableaux et les Sets stockent tous les deux une collection de valeurs, alors comment choisir ?
Optez pour un Set quand :
- Les valeurs doivent être uniques et vous voulez que le moteur s'en charge.
- Vous faites beaucoup de tests d'appartenance.
set.has(x)est en O(1) ;array.includes(x)est en O(n). Dans une boucle, l'écart se creuse très vite. - L'ordre d'insertion vous suffit. Un
Setitère dans l'ordre d'insertion, mais sans accès par index.
Restez sur un tableau quand :
- Vous avez besoin d'un accès positionnel —
arr[0], découpage, tri. - Les doublons ont du sens — un panier d'achat avec deux fois le même article.
- Vous utilisez beaucoup de méthodes de tableau comme
.map,.filter,.reduce. LesSetne les ont pas ; il faudrait d'abord les convertir en tableau avec le spread.
Un petit exemple côté performance :
Si banned était un tableau, chaque callback de filter devrait parcourir toute la liste. Avec un Set, la recherche se fait en temps constant.
Parcourir un Set
Même logique qu'avec une Map : for...of fonctionne directement, et le spread te redonne un tableau :
Les Set exposent aussi .keys(), .values() et .entries() par symétrie avec Map, même si pour un Set les clés et les valeurs sont la même chose. En pratique, on itère directement la plupart du temps.
Exemple concret : compter les visiteurs uniques par page
En combinant les deux, on obtient une Map qui associe chaque chemin de page à un Set d'identifiants de visiteurs :
La Map s'occupe de la correspondance chemin → bucket, et le Set gère la déduplication à l'intérieur de chaque bucket. On pourrait obtenir le même résultat avec un simple objet et des tableaux, bien sûr, mais il faudrait multiplier les appels à indexOf et les vérifications hasOwnProperty à chaque étape.
WeakMap et WeakSet, en bref
Deux collections apparentées existent pour un cas d'usage bien précis : WeakMap et WeakSet. Elles conservent des références faibles, c'est-à-dire qu'une entrée dont la clé (pour WeakMap) ou la valeur (pour WeakSet) n'est plus référencée ailleurs est automatiquement collectée par le ramasse-miettes.
Elles n'acceptent que des objets comme clés, ne sont pas itérables et n'exposent pas de .size. C'est un choix volontaire : si on pouvait les parcourir, le comportement du ramasse-miettes deviendrait observable. Elles sont utiles pour mettre en cache des métadonnées sur des objets qu'on ne contrôle pas, mais on les croise rarement dans le code du quotidien.
La suite : JSON
Map et Set sont parfaits en mémoire, mais aucun des deux ne survit tel quel à un JSON.stringify — les Map se transforment en {} et les Set aussi en {}. La page suivante est consacrée à JSON : comment sérialiser et parser des données, et quels patterns utiliser pour faire transiter les collections vues ici à travers un réseau ou un fichier.
Questions fréquentes
Quelle est la différence entre une Map et un objet en JavaScript ?
Une Map accepte n'importe quelle valeur comme clé — objets, fonctions, nombres, peu importe — alors qu'un objet classique convertit ses clés en chaînes (ou en symboles). La Map expose aussi directement sa taille via .size, itère dans l'ordre d'insertion et n'hérite d'aucune clé depuis un prototype : pas de risque de collision avec toString ou constructor. Bref, on sort la Map quand les clés ne sont pas des chaînes ou quand on ajoute/supprime des entrées en permanence.
À quoi sert un Set en JavaScript ?
Un Set stocke uniquement des valeurs uniques — les doublons sont ignorés sans rien dire. D'ailleurs, la façon la plus rapide de dédoublonner un tableau, c'est [...new Set(arr)]. En prime, .has() tourne en O(1), ce qui est bien plus rapide que array.includes() dès qu'on teste l'appartenance dans une boucle.
Comment parcourir une Map ?
Le for...of fonctionne directement : for (const [key, value] of myMap) déstructure chaque entrée au passage. On peut aussi itérer sur myMap.keys(), myMap.values() ou myMap.entries(). L'ordre d'itération suit toujours l'ordre d'insertion, alors qu'un objet classique ne le garantit pas quand les clés ressemblent à des nombres.