Itération, collections et Symbol
for…of, Map, Set, WeakMap, WeakSet et l'usage de Symbol pour des clés non énumérables.
Au-delà des simples objets {} et tableaux [], JavaScript propose des structures
de données supplémentaires — Map, Set, WeakMap, WeakSet — qui résolvent des
problèmes spécifiques (clés non-chainées, dictionnaires avec clés objet, mémoisation,
données associées à un objet sans empêcher la garbage collection). Ce bloc montre
quand utiliser quoi, et comment itérer correctement sur chaque structure.
🔄 Itérer sur un objet
Il existe plusieurs façons d'itérer sur un objet, chacune avec un comportement différent.
for…in — remonte la chaîne de prototypes
const parent = { heritage: true }
const enfant = Object.create(parent)
enfant.propre = "valeur"
for (const cle in enfant) {
console.log(cle) // "propre", "heritage"
}for…in itère sur toutes les clés énumérables, y compris celles héritées de la
chaîne prototype. Pour ne traiter que les propriétés propres, filtrer avec
hasOwnProperty :
for (const cle in enfant) {
if (enfant.hasOwnProperty(cle)) {
console.log(cle) // "propre" uniquement
}
}Object.keys() + for…of — seulement les clés propres
const user = { nom: "Alice", age: 25, ville: "Paris" }
for (const cle of Object.keys(user)) {
console.log(cle, user[cle])
}
// nom Alice
// age 25
// ville ParisPlus sûr que for…in car ne remonte pas la chaîne prototype.
Object.entries() — clé ET valeur
for (const [cle, valeur] of Object.entries(user)) {
console.log(`${cle}: ${valeur}`)
}Idéal pour debugger ou transformer un objet en autre chose (Map, tableau…).
Object.values() — uniquement les valeurs
const valeurs = Object.values(user) // ["Alice", 25, "Paris"]Utilitaire quand on a juste besoin des valeurs sans les clés.
for…of direct sur un objet ? Non.
for (const v of user) // TypeError : user is not iterableLes objets ne sont pas itérables par défaut. Il faut passer par Object.keys(),
Object.values(), ou Object.entries() pour itérer avec for…of.
for…of ≠ for…in
for…of itère sur les valeurs d'un itérable (tableau, Map, Set, string).
for…in itère sur les clés énumérables d'un objet (et remonte la chaîne).
Confondre les deux donne des résultats inattendus sur les tableaux :
for (const v of [1,2,3]) → 1, 2, 3 ; for (const v in [1,2,3]) → "0", "1", "2".
🗂️ Map — dictionnaire avec clés quelconques
Map est une collection clé-valeur où les clés peuvent être n'importe quel
type (objet, fonction, nombre, chaîne…) et qui préserve l'ordre d'insertion.
const cache = new Map()
// Clés objet (impossible avec un {} normal)
const cle1 = { id: 1 }
const cle2 = { id: 2 }
cache.set(cle1, "données pour A")
cache.set(cle2, "données pour B")
cache.has(cle1) // true
cache.get(cle1) // "données pour A"
cache.size // 2
cache.delete(cle2)
// Itération
for (const [cle, valeur] of cache) {
console.log(cle, valeur)
}
cache.forEach((valeur, cle) => {
console.log(cle, valeur)
})Map vs Object — quand utiliser quoi
| Critère | {} | Map |
|---|---|---|
| Clés | uniquement strings/symbols | n'importe quel type |
| Ordre d'insertion | partiellement (ES2015+) | garanti |
| Taille | Object.keys().length | map.size |
| Itération directe | via Object.keys/entries | for…of direct |
| Performance | optimisé moteur pour les strings | plus lent pour les strings purs |
Règle pratique : {} pour les données structurées avec des clés connues en
avance (config, données utilisateur typées). Map quand les clés sont dynamiques,
quand ce sont des objets, ou quand on a besoin d'un dictionnaire pur avec size.
Map pour la mémoisation
Map est souvent utilisé pour la mémoisation : const cache = new Map(), puis
if (cache.has(args)) return cache.get(args); cache.set(args, result);. Avec un
{} on serait limité aux clés strings ; avec Map on peut utiliser l'objet argument
lui-même comme clé.
🎒 Set — ensemble sans doublons
const emails = new Set()
emails.add("alice@exemple.com")
emails.add("bob@exemple.com")
emails.add("alice@exemple.com") // déjà présent — ignoré
emails.size // 2
emails.has("alice@exemple.com") // true
emails.delete("bob@exemple.com")
// Itération
for (const email of emails) {
console.log(email)
}
// Ensemble d'opérations
const a = new Set([1, 2, 3])
const b = new Set([2, 3, 4])
const intersection = new Set([...a].filter(x => b.has(x))) // {2, 3}
const union = new Set([...a, ...b]) // {1, 2, 3, 4}Set pour déduplication
new Set(tableau) supprime les doublons en une opération : const unique = [...new Set(listeDupliquee)]. C'est le moyen le plus concis de dédupliquer un
tableau de primitives en JavaScript moderne.
🕳️ WeakMap & WeakSet — références faibles
WeakMap et WeakSet sont des versions de Map et Set où les clés (et éléments
pour WeakSet) sont maintenues par référence faible : si l'objet clé n'est plus
referencié ailleurs, il peut être nettoyé par le garbage collector.
const wm = new WeakMap()
const element = document.querySelector("#mon-element")
wm.set(element, { clicCount: 0 })
// La donnée associée reste tant que element est en vie,
// et disparaît automatiquement quand element est retiré du DOMCas d'usage typiques :
- Associer des données privées à un objet DOM sans les exposer
- Mémoriser des informations sur des objets dont on ne contrôle pas la durée de vie
- Éviter les fuites mémoire (memory leaks) quand on cache des références à des objets temporaires
WeakMap n'est pas itérable
WeakMap n'expose pas de moyen d'itération (pas de keys(), values(),
entries(), pas de forEach). C'est un choix volontaire — si les clés peuvent
disparaître à tout moment, itérer serait inconsistant. Utiliser Map quand on
a besoin d'itérer.
🔣 Symbol — clés non énumérables
Symbol crée un identifiant unique, utilisable comme clé d'objet. Les propriétés
associées à un Symbol ne sont pas listées par Object.keys(), for…in, ou le
JSON.stringify (par défaut).
const ID = Symbol("id")
const user = {
[ID]: "user-1234",
nom: "Alice",
}
Object.keys(user) // ["nom"] — le Symbol n'apparaît pas
user[ID] // "user-1234" — accessible si on a la référence du Symbol
user.nom // "Alice"Symbol.iterator — rendre un objet itérable
const sequence = {
donnees: ["a", "b", "c"],
[Symbol.iterator]() {
let i = 0
const donnees = this.donnees
return {
next() {
if (i >= donnees.length) return { done: true }
return { value: donnees[i++], done: false }
},
}
},
}
for (const valeur of sequence) {
console.log(valeur) // "a", "b", "c"
}En pratique, peu de code crée son propre Symbol.iterator. Mais comprendre ce
mécanisme explique pourquoi for…of fonctionne sur les tableaux, Maps, Sets et
chaînes de caractères — tous exposent Symbol.iterator.
🛠️ Exercices pratiques
Map pour mémoisation et Set pour déduplication
Utiliser les collections Map, Set et WeakMap
Vous allez implémenter une fonction mémoïsée avec Map, dédupliquer un tableau avec Set, et associer des données à un objet via WeakMap.
- Utiliser Map pour la mémoisation (cache clé-valeur)
- Exploiter Set pour supprimer les doublons d'un tableau
- Associer des données à un objet via WeakMap (références faibles)
Partie A — Mémoisation avec Map
Implémentez memoize(fn) qui retourne une fonction mémoisée :
- utilise un
Mapcomme cache (clé = premier argument, valeur = résultat) - retourne le résultat depuis le cache si la clé existe, sinon calcule et stocke
Indice — Map comme cache
Utilisez cache.has(key) pour vérifier l'existence, cache.get(key) pour lire, et cache.set(key, value) pour stocker. Le premier argument de la fonction wrappée (args[0]) sert de clé.
Partie B — Déduplication avec Set
Écrivez dedupliquer(liste) qui utilise Set pour supprimer les doublons d'un tableau de primitives et retourne un nouveau tableau.
Indice — Set et spread
new Set(tableau) crée un ensemble sans doublons. L'opérateur spread [...ensemble] convertit le Set en tableau. En une ligne : return [...new Set(liste)].
Partie C — Données associées avec WeakMap
Créez un WeakMap nommé metadata et une fonction associerMetadata(obj, data) qui associe des données à un objet sans empêcher sa garbage collection.
Indice — WeakMap.set
Un WeakMap utilise .set(clé, valeur) et .get(clé). La clé DOIT être un objet (pas une primitive). Appelez metadata.set(obj, data) pour associer les données.
Code à valider
🧩 Synthèse du bloc
Itération objets
Object.keys/values/entries pour les propriétés propres ; for…in remonte la chaîne prototype (à filtrer).
Map
Dictionnaire avec clés de tout type, ordre garanti, taille directe, itérable. Idéal pour cache/mémoisation.
Set / WeakSet
Set = ensemble sans doublons, itérable. WeakSet = références faibles, non-itérable, pas de fuite mémoire.
WeakMap / Symbol
WeakMap = données associées à un objet sans empêcher le GC. Symbol = clés uniques non énumérables, Symbol.iterator pour l'itération personnalisée.
Quiz de fin de bloc
Quiz du chapitre
JavaScript avancé
Terminez le quiz du chapitre pour le marquer comme complété.