Comprends le protocole d'itération et crée des itérables personnalisés avec les fonctions génératrices en utilisant function* et yield.
Un itérateur est tout objet qui implémente une méthode next(). Chaque appel à next() retourne un objet avec deux propriétés :
value — la prochaine valeur dans la séquence.done — true si la séquence est terminée, false sinon.// Un itérateur manuel simple
const iterator = {
current: 1,
last: 3,
next() {
if (this.current <= this.last) {
return { value: this.current++, done: false };
}
return { value: undefined, done: true };
}
};
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }De nombreux types JavaScript intégrés sont itérables : tableaux, chaînes de caractères, Maps, Sets, NodeLists, et plus encore. La boucle for...of, l'opérateur de décomposition (...), et la déstructuration utilisent tous le protocole d'itération en coulisses.
for (const char of 'hello') {
console.log(char); // 'h', 'e', 'l', 'l', 'o'
}Un objet est itérable s'il a une méthode à la clé Symbol.iterator qui retourne un itérateur.
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
if (current <= last) {
return { value: current++, done: false };
}
return { value: undefined, done: true };
}
};
}
};Maintenant tu peux utiliser cet itérable personnalisé avec n'importe quelle fonctionnalité d'itération :
// for...of
for (const n of range) {
console.log(n); // 1, 2, 3, 4, 5
}
// Opérateur de décomposition
const nums = [...range]; // [1, 2, 3, 4, 5]
// Déstructuration
const [a, b, c] = range; // a=1, b=2, c=3
// Array.from
const arr = Array.from(range); // [1, 2, 3, 4, 5]Le protocole iterable est ce qui rend les fonctionnalités d'itération de JavaScript si flexibles et cohérentes. Tout objet qui implémente [Symbol.iterator]() peut participer à l'écosystème d'itération.
Écrire des itérateurs manuellement est verbeux. Les fonctions génératrices offrent une façon beaucoup plus simple de créer des itérateurs.
Une fonction génératrice est déclarée avec function* (note l'astérisque). À l'intérieur, tu utilises le mot-clé yield pour produire des valeurs.
function* countUp(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
const gen = countUp(1, 3);
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }Caractéristiques clés :
yield met en pause la fonction et produit une valeur.next() reprend l'exécution là où elle s'était arrêtée.for (const n of countUp(1, 5)) {
console.log(n); // 1, 2, 3, 4, 5
}
const nums = [...countUp(10, 15)]; // [10, 11, 12, 13, 14, 15]yield* délègue à un autre itérable :
function* combined() {
yield* [1, 2, 3];
yield* 'abc';
}
// Produit : 1, 2, 3, 'a', 'b', 'c'Les générateurs sont particulièrement puissants pour les scénarios qui bénéficient de l'évaluation paresseuse — produire des valeurs uniquement quand elles sont nécessaires.
Séquences infinies — génère des valeurs indéfiniment, en les consommant une à la fois :
function* ids() {
let id = 1;
while (true) {
yield id++;
}
}
const idGen = ids();
console.log(idGen.next().value); // 1
console.log(idGen.next().value); // 2
console.log(idGen.next().value); // 3
// Peut continuer indéfinimentSéquence de Fibonacci :
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}Traitement paresseux de grands ensembles de données — évite de tout charger en mémoire :
function* filterLarge(items, predicate) {
for (const item of items) {
if (predicate(item)) yield item;
}
}Pagination :
function* paginate(items, pageSize) {
for (let i = 0; i < items.length; i += pageSize) {
yield items.slice(i, i + pageSize);
}
}Générateurs asynchrones combinent les générateurs avec async/await pour diffuser des données asynchrones :
async function* fetchPages(url) {
let page = 1;
while (true) {
const response = await fetch(`${url}?page=${page}`);
const data = await response.json();
if (data.length === 0) return;
yield data;
page++;
}
}<div id="output"></div>
<script>
// Générateur Fibonacci
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
// Prend les N premières valeurs d'un générateur
function take(gen, n) {
const result = [];
for (const value of gen) {
result.push(value);
if (result.length >= n) break;
}
return result;
}
const first10 = take(fibonacci(), 10);
// Générateur range
function* range(start, end, step = 1) {
for (let i = start; i <= end; i += step) {
yield i;
}
}
const evens = [...range(0, 20, 2)];
const output = document.getElementById('output');
output.innerHTML = `
<p>Les 10 premiers Fibonacci : [${first10.join(', ')}]</p>
<p>Nombres pairs 0-20 : [${evens.join(', ')}]</p>
`;
</script>Que fait le mot-clé yield dans un générateur ?