Кратко
СкопированоМетод Iterator возвращает массив значений, полученных при обходе итератора. О том, что такое итератор, можно прочитать в статье «Итератор».
Пример
СкопированоПредположим у нас есть функция-генератор плана по изучению основ JavaScript.
function* getStudyPlan() { yield 'Переменные const, let и var' yield 'Выражения и инструкции' yield 'Области видимости'}
function* getStudyPlan() {
yield 'Переменные const, let и var'
yield 'Выражения и инструкции'
yield 'Области видимости'
}
Функция get возвращает итератор. Мы можешь переходить от темы к теме, вызывая метод next или for:
// Создаём итераторconst topics = getStudyPlan()console.log(topics.next().value)// Переменные const, let и varfor (const topic of topics) { console.log(topic)}// Выражения и инструкции// Области видимости
// Создаём итератор
const topics = getStudyPlan()
console.log(topics.next().value)
// Переменные const, let и var
for (const topic of topics) {
console.log(topic)
}
// Выражения и инструкции
// Области видимости
С помощью метода Iterator мы можем получить массив значений итератора:
// Создаём итераторconst topics = getStudyPlan()// Получим все значения итератора:console.log(topics.toArray())// [// 'Переменные const, let и var',// 'Выражения и инструкции',// 'Области видимости'// ]
// Создаём итератор
const topics = getStudyPlan()
// Получим все значения итератора:
console.log(topics.toArray())
// [
// 'Переменные const, let и var',
// 'Выражения и инструкции',
// 'Области видимости'
// ]
Как пишется
Скопированоto не имеет аргументов.
to возвращает массив всех значений итератора.
Как понять
СкопированоОбъект-итератор не хранит коллекцию значений, а лишь предоставляет последовательный доступ к элементам коллекции. Такой подход позволяет не тратить лишнюю память, так как часто при переборе необходимо только текущее значение, а не вся коллекция целиком. Однако иногда вместо итерации по коллекции требуется получить все её значения.
Для получения массива значений итератора можно использовать несколько подходов. Рассмотрим это на примере.
Допустим у нас множество значений в виде Set-объект:
// Создаём множествоconst byteSet = new Set()// Наполняем множество значениямиbyteSet.add('000') // 0byteSet.add('001') // 1byteSet.add('010') // 2byteSet.add('011') // 3
// Создаём множество
const byteSet = new Set()
// Наполняем множество значениями
byteSet.add('000') // 0
byteSet.add('001') // 1
byteSet.add('010') // 2
byteSet.add('011') // 3
Создадим итератор множества и получим массив его значений.
- Применяя деструктуризацию.
const byteIterator = byteSet.values()const arr1 = [ ...byteIterator ]console.log(arr1)// [ '000', '001', '010', '011' ]
const byteIterator = byteSet.values()
const arr1 = [ ...byteIterator ]
console.log(arr1)
// [ '000', '001', '010', '011' ]
- Используя метод Array.from().
const byteIterator = byteSet.values()const arr2 = Array.from(byteIterator)console.log(arr2)// [ '000', '001', '010', '011' ]
const byteIterator = byteSet.values()
const arr2 = Array.from(byteIterator)
console.log(arr2)
// [ '000', '001', '010', '011' ]
Метод итератора to решает ту же задачу:
const byteIterator = byteSet.values()const arr3 = byteIterator.toArray()console.log(arr3)// [ '000', '001', '010', '011' ]
const byteIterator = byteSet.values()
const arr3 = byteIterator.toArray()
console.log(arr3)
// [ '000', '001', '010', '011' ]
☝️ Говоря о методах итератора, важно помнить разницу между объектом, реализующим протокол итератора, итерируемым объектом и итератором как объектом-наследником Iterator. Подробнее об этом можно прочитать в статье «Итератор».
В отличии от Array метод to можно вызвать для объекта, реализующего протокол итератора, но не являющегося итерируемым.
Предположим мы хотим работать с представлением чисел в двоичном виде. Напишем функцию, создания объекта реализующего протокол итератора:
function getByteBinary(max = 7) { let number = 0 const size = max.toString(2).length return { next() { if (number > max) { return { done: true } } // Преобразуем число в строку в двоичном виде const value = number.toString(2).padStart(size, '0') number++ return { value, done: false } } }}const iterator = getByteBinary(2)console.log(iterator.next())// { value: '00', done: false }console.log(iterator.next())// { value: '01', done: false }console.log(iterator.next())// { value: '10', done: false }console.log(iterator.next())// { done: true }
function getByteBinary(max = 7) {
let number = 0
const size = max.toString(2).length
return {
next() {
if (number > max) {
return { done: true }
}
// Преобразуем число в строку в двоичном виде
const value = number.toString(2).padStart(size, '0')
number++
return { value, done: false }
}
}
}
const iterator = getByteBinary(2)
console.log(iterator.next())
// { value: '00', done: false }
console.log(iterator.next())
// { value: '01', done: false }
console.log(iterator.next())
// { value: '10', done: false }
console.log(iterator.next())
// { done: true }
Возвращаемый функцией get объект имеет метод next, но не является итерируемым, потому что не содержит метод [. Попытка использовать цикл for или применить деструктуризацию приведёт к ошибке:
for (const x of iterator) { console.log(x)}// TypeError: iterator is not iterableconsole.log([...iterator])// TypeError: iterator is not iterable
for (const x of iterator) {
console.log(x)
}
// TypeError: iterator is not iterable
console.log([...iterator])
// TypeError: iterator is not iterable
Создать массив значений итератора с помощью Array тоже не получится:
console.log(Array.from(iterator))// []
console.log(Array.from(iterator))
// []
Однако мы можем получить массив значений с помощью Iterator. Метод to не содержится в цепочке прототипов объекта iterator, но может быть вызван через call:
console.log(Iterator.prototype.toArray.call(iterator))// [ '00', '01', '10' ]
console.log(Iterator.prototype.toArray.call(iterator))
// [ '00', '01', '10' ]
Это работает, так как при выполнении to от объекта требуется только иметь метод next.
Подсказки
Скопировано💡 Метод to возвращает массив всех значений, только в случае когда итератор находится в начальным состоянии. Значения, полученные при вызовах next не попадут в массив:
function* getPrimeNumbers() { yield 2 yield 3 yield 5 yield 7}const numbers = getPrimeNumbers()console.log(numbers.next().value)// 2console.log(numbers.toArray())// [ 3, 5, 7 ]
function* getPrimeNumbers() {
yield 2
yield 3
yield 5
yield 7
}
const numbers = getPrimeNumbers()
console.log(numbers.next().value)
// 2
console.log(numbers.toArray())
// [ 3, 5, 7 ]
💡 Метод to не защищён от вызова на «бесконечном» итераторе. Бесконечным называется итератор, не имеющий конечного состояния ({ done).
Например функция-генератор случайных чисел:
function* getRandomNumber() { while (true) { yield Math.floor(Math.random() * 1000) }}
function* getRandomNumber() {
while (true) {
yield Math.floor(Math.random() * 1000)
}
}
В нашей функции нет условия прекращения цикла поэтому при вызове она вернёт итератор, поддерживающий бесконечное количество вызовов next. При попытке получить массив значений «бесконечного» итератора to зависнет в бесконечном цикле и может вызвать переполнение памяти:
const infinityIterator = getRandomNumber()// Внимание! Небезопасное взаимодействие с бесконечностью!console.log(infinityIterator.toArray()) // ⚠️ Никогда не завершится!
const infinityIterator = getRandomNumber()
// Внимание! Небезопасное взаимодействие с бесконечностью!
console.log(infinityIterator.toArray()) // ⚠️ Никогда не завершится!
- Chrome 122, поддерживается
- Edge 122, поддерживается
- Firefox 131, поддерживается
- Safari 18.4, поддерживается