TW3 — Technologies du Web 3

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 Paris

Plus 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 iterable

Les 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ésuniquement strings/symbolsn'importe quel type
Ordre d'insertionpartiellement (ES2015+)garanti
TailleObject.keys().lengthmap.size
Itération directevia Object.keys/entriesfor…of direct
Performanceoptimisé moteur pour les stringsplus 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 DOM

Cas 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 Map comme 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

javascript

🧩 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

2 questions
0 / 2 répondue0%
  1. 1Quelle méthode transforme chaque élément d'un tableau ?
  2. 2Que fait filter() sur un tableau ?
Répondez à toutes les questions pour valider.

JavaScript avancé

Terminez le quiz du chapitre pour le marquer comme complété.

Tableau de bord

On this page