En JavaScript, un dictionnaire JavaScript sert à associer une clé à une valeur, mais le vrai enjeu n’est pas le nom de la structure. Ce qui compte, c’est de choisir entre un objet simple, une Map ou une WeakMap selon le besoin réel: affichage, cache, index, métadonnées ou échange de données avec une API. Je vais aller droit au but avec les différences utiles, les pièges courants et quelques cas concrets que l’on rencontre souvent en développement web.
Voici l’essentiel à retenir avant de choisir une structure clé-valeur
-
Objectconvient bien aux données structurées et au JSON, surtout si les clés restent textuelles. -
Mapest plus propre pour les collections dynamiques, les suppressions fréquentes et les clés non string. -
WeakMapsert surtout à attacher des données à des objets sans bloquer leur collecte mémoire. - Si les clés viennent de l’extérieur, un objet classique peut poser des problèmes de prototype et de collision.
- L’ordre d’insertion est plus lisible et plus prévisible avec
Mapqu’avec un objet. - Pour une application web, le bon choix dépend autant de la forme des données que de leur cycle de vie.
Ce qu’un dictionnaire en JavaScript fait vraiment
Quand on parle de structure clé-valeur, on parle d’un mécanisme simple: une clé identifie une valeur. En pratique, JavaScript propose plusieurs façons de le faire, mais elles ne se valent pas. Un objet littéral peut jouer ce rôle, une Map le fait de manière plus explicite, et une WeakMap ajoute une contrainte mémoire très utile dans certains cas.
Le point qui mérite d’être compris tout de suite, c’est qu’un objet n’est pas un sac vide. Il hérite de Object.prototype, ce qui veut dire qu’il embarque déjà des propriétés et des méthodes. Pour des données internes bien contrôlées, ce n’est pas un problème. Pour un dictionnaire alimenté par des clés externes, c’est une source de surprises que j’évite dès que possible.
Autre différence importante: avec un objet, les clés sont essentiellement des chaînes de caractères ou des symboles, et les nombres sont convertis en chaînes. Avec Map, la clé peut être pratiquement n’importe quelle valeur, y compris un objet. Cette nuance change vite la façon de concevoir un cache, un index ou une table de correspondance.
En clair, le sujet n’est pas seulement de stocker une valeur. Il faut aussi penser à la forme des clés, à l’itération, à la sécurité et à la durée de vie des données. C’est justement là que le choix entre objet et Map devient utile.
Quand Map vaut mieux qu’un objet
Je choisis Map dès que la collection ressemble à un véritable registre dynamique: ajout fréquent, suppression fréquente, itération régulière et besoin d’un ordre d’insertion clair. La structure est faite pour ça, et le code devient souvent plus lisible.
-
Clés variées si les clés ne sont pas forcément des chaînes,
Mapévite les conversions implicites. -
Taille directe avec
map.size, on lit immédiatement le nombre d’entrées. -
Itération simple
Mapse parcourt naturellement avecfor...of. - Ordre stable l’ordre d’insertion est conservé, ce qui aide pour les affichages et les exports.
-
Nettoyage clair
set,deleteetclearrendent l’intention très explicite.
Voici un exemple que j’utilise souvent pour un comptage simple:
const counts = new Map();
for (const tag of tags) {
counts.set(tag, (counts.get(tag) ?? 0) + 1);
}Le code reste compact, mais il ne cache rien. On voit immédiatement qu’on compte des occurrences, pas qu’on détourne un objet pour faire la même chose. C’est précisément ce genre de lisibilité qui fait gagner du temps sur une base de code qui grandit.
À l’inverse, si je sais déjà que les clés seront des identifiants textuels destinés à être envoyés en JSON, un objet simple peut rester plus adapté. Le bon réflexe n’est donc pas de bannir l’objet, mais de réserver Map aux collections qui méritent vraiment ce modèle. La comparaison directe aide à trancher plus vite.

Comparer Object, Map et WeakMap sans perdre le fil
Je préfère mettre ces trois structures côte à côte, parce que beaucoup de confusions viennent d’un mauvais alignement entre besoin réel et outil choisi. Un objet n’est pas une Map simplifiée, et une WeakMap ne remplace ni l’une ni l’autre. Chacune résout un problème différent.
| Critère | Object |
Map |
WeakMap |
|---|---|---|---|
| Type de clés | Chaînes et symboles, avec conversion des nombres en chaînes | Pratiquement n’importe quelle valeur | Objets et symboles non enregistrés |
| Itération | Oui, mais avec prudence sur les clés héritées et l’ordre | Oui, naturellement | Non |
| Ordre | Règles plus nuancées, surtout pour les clés numériques | Ordre d’insertion | Pas d’itération, donc pas d’ordre exploitable |
| Taille | Via Object.keys(obj).length ou équivalent |
size |
Aucune propriété de taille |
| JSON | Naturellement compatible | Conversion nécessaire | Pas adapté |
| Cas idéal | Données structurées, configuration, réponse API | Cache, index, comptage, collections vivantes | Métadonnées liées à des objets, cache mémoire sensible |
| Piège principal | Prototype, collision de noms, clés implicites | Conversion avant sérialisation | Impossible à parcourir, impossible à compter directement |
Il existe aussi un bon compromis quand on veut garder un objet sans héritage: Object.create(null). Je l’utilise quand je veux une vraie table de correspondance à clés textuelles, sans propriétés héritées comme toString ou constructor. C’est une option discrète, mais très utile lorsque les clés viennent d’une source externe.
Je retiens surtout une règle simple: si je dois manipuler une collection, j’oriente vers Map; si je dois représenter une donnée structurée ou un payload JSON, je garde un objet. Cette distinction évite déjà une bonne partie des erreurs de conception. Pour les cas où la mémoire devient centrale, il reste un troisième outil à regarder.
Quand WeakMap résout un problème que les autres structures ne couvrent pas
WeakMap est souvent mal comprise parce qu’on la compare à Map alors qu’elle ne sert pas au même usage. Elle associe une valeur à un objet clé, mais sans empêcher cet objet d’être libéré par le ramasse-miettes. Autrement dit, elle est faite pour des données accessoires, pas pour des inventaires persistants.
C’est très pratique pour attacher un état privé à des nœuds DOM, à des instances de classe ou à des objets temporaires venant d’un framework. Dès que l’objet clé disparaît, la donnée associée devient elle aussi récupérable par le moteur. Je m’en sers volontiers pour limiter les risques de fuite mémoire dans des interfaces qui vivent longtemps.
const stateByNode = new WeakMap();
function register(node) {
stateByNode.set(node, { open: false });
}Il faut en revanche accepter ses limites: pas d’itération, pas de compteur global, pas d’export direct. Si vous avez besoin de lister les entrées, ce n’est pas le bon outil. J’insiste sur ce point parce que beaucoup de développeurs essaient d’y voir un substitut discret à Map, alors que son objectif principal est différent.
Dans une application web, WeakMap devient intéressant quand les données doivent suivre le cycle de vie d’un objet plutôt que celui de l’application entière. C’est une distinction subtile, mais elle change la qualité d’une architecture quand les composants se multiplient.
Les cas d’usage qui reviennent dans un projet web
Dans les projets que je relis, les mêmes besoins reviennent souvent: compter, indexer, mémoriser temporairement et préparer des données pour l’affichage. C’est là qu’une structure clé-valeur bien choisie fait gagner en clarté.
Compter des occurrences sans ambiguïté
Le comptage est un bon terrain pour Map. On veut une logique directe, des clés variées et un accès simple à la valeur courante. C’est typiquement le cas pour des tags, des catégories ou des mots-clés récupérés depuis une API.
const counts = new Map();
for (const item of items) {
const current = counts.get(item.type) ?? 0;
counts.set(item.type, current + 1);
}Le résultat est clair: chaque type a son compteur, sans bricolage autour des propriétés d’un objet. Dans un tableau de bord ou un filtre de recherche, cette approche reste facile à faire évoluer.
Indexer des entités par identifiant
Si je dois retrouver rapidement un utilisateur, un produit ou une commande à partir d’un identifiant, un objet peut suffire. Je choisis souvent Object.create(null) pour éviter l’héritage, surtout si les identifiants sont générés ou fournis par une source externe.
const usersById = Object.create(null);
for (const user of users) {
usersById[user.id] = user;
}Cette forme est très efficace pour une lecture directe et pour une exportation JSON. Le point important, c’est de garder en tête que l’objet sert ici de table de correspondance simple, pas de collection générique.
Lire aussi : Gérer l'IP en PHP - Évitez les pièges, sécurisez vos apps
Attacher des métadonnées à des objets
Pour les nœuds DOM, les instances ou les objets temporaires, WeakMap apporte une propreté que je trouve difficile à remplacer. On stocke une information annexe sans créer de référence forte qui pourrait bloquer le nettoyage mémoire.
const meta = new WeakMap();
function track(node) {
meta.set(node, {
touchedAt: Date.now(),
source: "ui",
});
}Ce cas d’usage est intéressant parce qu’il évite de polluer l’objet cible avec des propriétés techniques. Je préfère cette approche quand l’état est strictement interne et qu’il n’a aucune raison d’être exposé ailleurs.
Ces trois exemples couvrent déjà une large part des besoins réels en développement web. La prochaine étape consiste surtout à éviter les erreurs de lecture et les raccourcis qui donnent l’impression de fonctionner jusqu’au jour où ils cassent.
Les pièges qui font perdre du temps
Les erreurs les plus fréquentes ne viennent pas d’un manque de syntaxe, mais d’un mauvais modèle mental. On croit utiliser une structure de données alors qu’on détourne un mécanisme de propriété, ou on oublie que certaines opérations n’ont pas le même sens selon le type choisi.
-
Confondre absence et valeur
undefinedavec un objet ou uneMap, il faut distinguer “la clé n’existe pas” de “la clé existe mais vautundefined”. -
Oublier les clés héritées sur un objet classique, des noms comme
constructoroutoStringpeuvent créer des ambiguïtés. -
Utiliser
for...insans filtre sur un objet, alors qu’on ne veut souvent que les propriétés propres. -
Oublier la conversion avant JSON une
Mapne se sérialise pas proprement sans étape intermédiaire. -
Attendre une taille ou une itération sur
WeakMapce n’est pas son rôle, et le moteur est libre de libérer les clés.
Pour un objet utilisé comme dictionnaire, j’ai aussi un réflexe simple: vérifier les clés avec Object.hasOwn(obj, key) plutôt que de me fier à une propriété héritée. Cette habitude réduit les surprises quand les données viennent d’un formulaire, d’une API ou d’un système de configuration partiellement contrôlé.
Autre point pratique: si vous avez besoin d’échanger les données avec le serveur, Object reste souvent le chemin le plus direct. Si vous avez besoin de conserver des paires clé-valeur côté client puis de les envoyer, convertissez explicitement la structure au lieu de compter sur un comportement implicite. Cette discipline évite des bugs d’intégration assez pénibles à déboguer.
Ma règle simple pour choisir la bonne structure
Quand je dois trancher vite, j’applique une petite grille de lecture. Elle ne remplace pas l’analyse du besoin, mais elle évite de sur-architecturer ou de choisir un type de collection par habitude.
- Si les clés sont des objets ou des valeurs non textuelles, je pars sur
Map. - Si la donnée doit devenir du JSON ou ressemble à un payload d’API, je garde un objet.
- Si les entrées sont nombreuses, dynamiques et souvent supprimées,
Mapreste plus lisible. - Si je veux attacher un état privé à un objet sans le retenir en mémoire, je prends
WeakMap. - Si les clés viennent d’une source externe mais restent textuelles, j’utilise soit
Map, soitObject.create(null).
En pratique, ma préférence est assez stable: Map pour les collections vivantes, Object pour les structures de données, WeakMap pour les attaches discrètes liées au cycle de vie. Cette hiérarchie me semble plus fiable que les règles simplistes du type “Map est toujours mieux”. Ce n’est pas vrai, et il vaut mieux le dire franchement.
Je garde aussi un critère très concret en tête: si quelqu’un doit relire le code dans six mois, quelle structure lui fera comprendre l’intention le plus vite? C’est souvent là que se joue la vraie qualité d’une base code, bien plus que dans une micro-optimisation isolée.
Ce que je retiens pour une base clé-valeur propre et durable
Un bon dictionnaire JavaScript n’est pas celui qui “marche” seulement dans le cas le plus simple. C’est celui qui reste lisible quand les données deviennent plus variées, quand les clés viennent de l’extérieur et quand l’application commence à vivre plus longtemps que prévu.
Si je devais résumer ma position en une phrase, ce serait celle-ci: je ne choisis pas une structure parce qu’elle existe, je la choisis parce qu’elle correspond au cycle de vie et à la forme des données. C’est cette discipline qui fait la différence entre un code qui tient par chance et un code qui tient parce qu’il est bien pensé.