Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Ітератор». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Ітератор, це об'єкт із методом `next()`, який щоразу повертає наступний елемент послідовності у вигляді `{ value, done }`.** Поки є що віддавати, `done` дорівнює `false`, а коли послідовність вичерпана, приходить `{ value: undefined, done: true }`, і перебір зупиняється. Ітератор не плутають з iterable: iterable, це об'єкт, який має метод `[Symbol.iterator]()`, що створює ітератор. Саме цей метод рушій викликає автоматично для `for...of`, spread `...`, `Array.from()` і деструктуризації. Вбудовані `Array`, `String`, `Map`, `Set`, `arguments`, `NodeList` і типізовані масиви є iterable, а звичайний об'єкт `{}` ні. Найпростіший спосіб створити ітератор, це генератор `function*`. ```javascript const it = ['a', 'b'][Symbol.iterator](); console.log(it.next()); // { value: 'a', done: false } console.log(it.next()); // { value: 'b', done: false } console.log(it.next()); // { value: undefined, done: true } ``` **Ключове:** ітератор віддає значення по одному через `next()`, а iterable вміє такий ітератор створити через `[Symbol.iterator]()`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Ітератор (iterator), це об'єкт, який почергово повертає елементи якоїсь послідовності на кожен виклик методу `next()`.** Формально: ітератор, це об'єкт, що реалізує метод `next()`, який повертає об'єкт вигляду `{ value: any, done: boolean }`. ## Теорія ### TL;DR - Ітератор, це об'єкт із методом `next()`, що повертає `{ value, done }`. - Коли `done: true`, ітерація завершується, а `value` тоді зазвичай `undefined`. - Iterable, це об'єкт, у якого є метод `[Symbol.iterator]()`, що повертає ітератор. - `for...of`, spread `...`, `Array.from()` і деструктуризація працюють саме через цей протокол. - Вбудовані iterable: `Array`, `String`, `Map`, `Set`, `arguments`, `NodeList`, типізовані масиви. - Генератор `function*` створює ітератор автоматично, без ручного `return { value, done }`. ### Швидкий приклад ```javascript function createIterator(arr) { let i = 0; return { next() { if (i < arr.length) { return { value: arr[i++], done: false }; } else { return { value: undefined, done: true }; } } }; } const iterator = createIterator(['a', 'b', 'c']); console.log(iterator.next()); // { value: 'a', done: false } console.log(iterator.next()); // { value: 'b', done: false } console.log(iterator.next()); // { value: 'c', done: false } console.log(iterator.next()); // { value: undefined, done: true } ``` Коли `done: true`, ітерація закінчується. ### Що робить ітератор усередині `for...of` Коли ви пишете: ```javascript for (const item of ['a', 'b', 'c']) { console.log(item); } ``` під капотом JavaScript робить приблизно таке: ```javascript const iterable = ['a', 'b', 'c']; const iterator = iterable[Symbol.iterator](); // створює ітератор while (true) { const { value, done } = iterator.next(); if (done) break; console.log(value); } ``` Тобто цикл сам не знає нічого про масиви: він знає лише протокол `next()`. ### Iterable і `Symbol.iterator` Об'єкт є **ітерованим (iterable)**, якщо в нього є метод: ```javascript obj[Symbol.iterator]() ``` який повертає **ітератор**. `Symbol.iterator`, це спеціальний символ, що визначає «ітерабельність» об'єкта, і JavaScript викликає цей метод автоматично, коли треба перебрати об'єкт. Приклади вбудованих ітерованих структур: - `Array` - `String` - `Map` - `Set` - `arguments` - `NodeList` (у браузері) - `TypedArray` Власний iterable пишеться так: ```javascript const range = { from: 1, to: 4, [Symbol.iterator]() { let current = this.from; let last = this.to; return { next() { if (current <= last) { return { value: current++, done: false }; } else { return { done: true }; } } }; } }; for (const num of range) { console.log(num); // 1, 2, 3, 4 } ``` Коли JavaScript зустрічає `for...of`, він викликає `range[Symbol.iterator]()` і використовує повернений ітератор. Рядок теж є iterable, причому він перебирається за кодовими точками, а не за байтами: ```javascript const str = 'Hi'; const iterator = str[Symbol.iterator](); console.log(iterator.next()); // { value: 'H', done: false } console.log(iterator.next()); // { value: 'i', done: false } console.log(iterator.next()); // { value: undefined, done: true } ``` ### Ітератори разом зі spread, деструктуризацією та `Array.from` Усі ці конструкції **внутрішньо використовують ітератор**: ```javascript const set = new Set([1, 2, 3]); console.log([...set]); // [1, 2, 3] console.log(Array.from(set)); // [1, 2, 3] const [a, b] = set; console.log(a, b); // 1 2 ``` Саме тому spread працює з `Set` і `Map`, хоча вони не є масивами: їм достатньо мати `[Symbol.iterator]()`. ### Генератори, це автоматичні ітератори Генератор (`function*`), це функція, яка сама створює ітератор. ```javascript function* numbers() { yield 1; yield 2; yield 3; } const iterator = numbers(); 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 } ``` А також: ```javascript for (const n of numbers()) { console.log(n); // 1, 2, 3 } ``` Генератори зручні тим, що не треба вручну писати `return { value, done }`: рушій формує ці об'єкти сам із кожного `yield`. Ітерабельність сучасних структур: | Структура | Ітерована? | Повертає | | --- | --- | --- | | `Array` | так | значення | | `String` | так | символи | | `Map` | так | пари `[key, value]` | | `Set` | так | значення | | `Object` | ні | нічого, але можна додати `[Symbol.iterator]()` вручну | | `arguments` | так | значення аргументів | Короткий підсумок: | Термін | Опис | | --- | --- | | **Ітератор** | Об'єкт із методом `next()`, що повертає `{ value, done }` | | **Ітерований об'єкт** | Об'єкт, у якого є метод `[Symbol.iterator]()` | | `Symbol.iterator` | Символ, що вказує на метод, який створює ітератор | | **Використовується в** | `for...of`, spread, `Array.from`, деструктуризації | | **Генератор (`function*`)** | Спрощений спосіб створити ітератор | ### Типові помилки - **Плутати ітератор з iterable.** Ітератор має `next()`, iterable має `[Symbol.iterator]()`. Масив, це iterable, а `arr[Symbol.iterator]()`, це вже ітератор. - **Очікувати, що `for...of` працюватиме зі звичайним об'єктом.** У `{}` немає `[Symbol.iterator]()`, тому буде `TypeError`. Для об'єктів беруть `Object.keys()`, `Object.values()` або `Object.entries()`. - **Повторно перебирати один і той самий ітератор.** Він одноразовий: після `{ done: true }` він назавжди вичерпаний, і другий `for...of` не дасть нічого. - **Забувати повертати `{ value, done }` саме як об'єкт.** Якщо `next()` поверне просте значення, рушій кине `TypeError`. - **Плутати `for...of` і `for...in`.** `for...in` перебирає ключі, включно з успадкованими, і жодного стосунку до протоколу ітерації не має. - **Писати нескінченний iterable без обмеження.** Генератор із `while (true)` у `for...of` без `break` або без `take` повісить вкладку.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.