Что такое итератор в javascript
Перейти к содержимому

Что такое итератор в javascript

  • автор:

Итераторы

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Более новая информация по этой теме находится на странице https://learn.javascript.ru/iterable.

В современный JavaScript добавлена новая концепция «итерируемых» (iterable) объектов.

Итерируемые или, иными словами, «перебираемые» объекты – это те, содержимое которых можно перебрать в цикле.

Например, перебираемым объектом является массив. Но не только он. В браузере существует множество объектов, которые не являются массивами, но содержимое которых можно перебрать (к примеру, список DOM-узлов).

Для перебора таких объектов добавлен новый синтаксис цикла: for..of .

'use strict'; let arr = [1, 2, 3]; // массив — пример итерируемого объекта for (let value of arr) < alert(value); // 1, затем 2, затем 3 >

Также итерируемой является строка:

'use strict'; for (let char of "Привет") < alert(char); // Выведет по одной букве: П, р, и, в, е, т >

Итераторы – расширяющая понятие «массив» концепция, которая пронизывает современный стандарт JavaScript сверху донизу.

Практически везде, где нужен перебор, он осуществляется через итераторы. Это включает в себя не только строки, массивы, но и вызов функции с оператором spread f(. args) , и многое другое.

В отличие от массивов, «перебираемые» объекты могут не иметь «длины» length . Как мы увидим далее, итераторы дают возможность сделать «перебираемыми» любые объекты.

Свой итератор

Допустим, у нас есть некий объект, который надо «умным способом» перебрать.

Например, range – диапазон чисел от from до to , и мы хотим, чтобы for (let num of range) «перебирал» этот объект. При этом под перебором мы подразумеваем перечисление чисел от from до to .

Объект range без итератора:

let range = < from: 1, to: 5 >; // хотим сделать перебор // for (let num of range) . 

Для возможности использовать объект в for..of нужно создать в нём свойство с названием Symbol.iterator (системный символ).

При вызове метода Symbol.iterator перебираемый объект должен возвращать другой объект («итератор»), который умеет осуществлять перебор.

По стандарту у такого объекта должен быть метод next() , который при каждом вызове возвращает очередное значение и проверяет, окончен ли перебор.

В коде это выглядит следующим образом:

'use strict'; let range = < from: 1, to: 5 >// сделаем объект range итерируемым range[Symbol.iterator] = function() < let current = this.from; let last = this.to; // метод должен вернуть объект с методом next() return < next() < if (current ; > else < return < done: true >; > > > >; for (let num of range) < alert(num); // 1, затем 2, 3, 4, 5 >

Как видно из кода выше, здесь имеет место разделение сущностей:

  • Перебираемый объект range сам не реализует методы для своего перебора.
  • Для этого создаётся другой объект, который хранит текущее состояние перебора и возвращает значение. Этот объект называется итератором и возвращается при вызове метода range[Symbol.iterator] .
  • У итератора должен быть метод next() , который при каждом вызове возвращает объект со свойствами:
    • value – очередное значение,
    • done – равно false если есть ещё значения, и true – в конце.

    Конструкция for..of в начале своего выполнения автоматически вызывает Symbol.iterator() , получает итератор и далее вызывает метод next() до получения done: true . Такова внутренняя механика. Внешний код при переборе через for..of видит только значения.

    Такое отделение функциональности перебора от самого объекта даёт дополнительную гибкость. Например, объект может возвращать разные итераторы в зависимости от своего настроения и времени суток. Однако, бывают ситуации когда оно не нужно.

    Если функциональность по перебору (метод next ) предоставляется самим объектом, то можно вернуть this в качестве итератора:

    'use strict'; let range = < from: 1, to: 5, [Symbol.iterator]() < return this; >, next() < if (this.current === undefined) < // инициализация состояния итерации this.current = this.from; >if (this.current ; > else < // очистка текущей итерации delete this.current; return < done: true >; > > >; for (let num of range) < alert(num); // 1, затем 2, 3, 4, 5 >// Произойдёт вызов Math.max(1,2,3,4,5); alert( Math.max(. range) ); // 5 (*)

    При таком подходе сам объект и хранит состояние итерации (текущий перебираемый элемент).

    В данном случае это работает, но для большей гибкости и понятности кода рекомендуется, всё же, выделять итератор в отдельный объект со своим состоянием и кодом.

    Оператор spread . и итераторы

    В последней строке (*) примера выше можно видеть, что итерируемый объект передаётся через spread для Math.max .

    При этом . range интерпретируется как последовательность чисел. То есть произойдёт цикл for..of по range , и его результаты будут использованы в качестве списка аргументов.

    Бесконечные итераторы

    Возможны и бесконечные итераторы. Например, пример выше при range.to = Infinity будет таковым. Или можно сделать итератор, генерирующий бесконечную последовательность псевдослучайных чисел. Тоже полезно.

    Нет никаких ограничений на next , он может возвращать всё новые и новые значения, и это нормально.

    Разумеется, цикл for..of по такому итератору тоже будет бесконечным, нужно его прерывать, например, через break .

    Встроенные итераторы

    Встроенные в JavaScript итераторы можно получить и явным образом, без for..of , прямым вызовом Symbol.iterator .

    Например, этот код получает итератор для строки и вызывает его полностью «вручную»:

    'use strict'; let str = "Hello"; // Делает то же, что и // for (var letter of str) alert(letter); let iterator = str[Symbol.iterator](); while(true) < let result = iterator.next(); if (result.done) break; alert(result.value); // Выведет все буквы по очереди >

    То же самое будет работать и для массивов.

    Итого

    • Итератор – объект, предназначенный для перебора другого объекта.
    • У итератора должен быть метод next() , возвращающий объект , где value – очередное значение, а done: true в конце.
    • Метод Symbol.iterator предназначен для получения итератора из объекта. Цикл for..of делает это автоматически, но можно и вызвать его напрямую.
    • В современном стандарте есть много мест, где вместо массива используются более абстрактные «итерируемые» (со свойством Symbol.iterator ) объекты, например оператор spread . .
    • Встроенные объекты, такие как массивы и строки, являются итерируемыми, в соответствии с описанным выше.

    Что такое итератор в javascript

    [Коллекции] Итератор в JavaScript: что это такое и как использовать

    [Коллекции] Итератор в JavaScript: что это такое и как использовать

    13 марта 2023
    Оценки статьи
    Еще никто не оценил статью

    Итератор — это объект в JavaScript, который обеспечивает последовательный доступ к элементам коллекции.

    В этой статье мы рассмотрим, что такое итераторы, как использовать их на разных типах коллекций, и как итераторы помогают упростить код и сделать его более читабельным.

    Что такое итераторы?

    Итераторы — это объекты, которые возвращают последовательные значения из коллекции. Они предоставляют метод next() , который возвращает следующий элемент коллекции и свойство done , которое указывает, достигнут ли конец коллекции.

    Итераторы используются в цикле for. of , чтобы перебирать элементы коллекции. Цикл for. of автоматически вызывает метод next() для каждого элемента коллекции и останавливается, когда свойство done возвращаемого объекта установлено в true .

    Пример использования итератора на массиве

    Давайте рассмотрим пример использования итератора на массиве:

    const arr = [1, 2, 3]; const iter = arr[Symbol.iterator]();  for (const item of iter)   console.log(item); > 

    Здесь мы создаем итератор для массива arr с помощью метода Symbol.iterator() . Затем мы используем цикл for. of , чтобы перебрать элементы массива. Каждый элемент выводится в консоль.

    Пример использования итератора на строке

    Итераторы могут быть использованы и на строках. Рассмотрим следующий пример:

    const str = "hello"; const iter = str[Symbol.iterator]();  let result = iter.next(); while (!result.done)   console.log(result.value);  result = iter.next(); > 

    Здесь мы создаем итератор для строки str , используя метод Symbol.iterator() . Затем мы используем цикл while , чтобы перебрать каждый символ строки. Каждый символ выводится в консоль.

    Пример использования итератора на объекте

    Итераторы также могут быть использованы на объектах. Рассмотрим следующий пример:

    const obj =  a: 1, b: 2, c: 3 >; const iter = Object.values(obj)[Symbol.iterator]();  for (const item of iter)   console.log(item); > 

    Здесь мы создаем итератор для значений объекта obj с помощью метода Object.values() . Затем мы используем цикл for. of , чтобы перебрать каждое значение объекта. Каждое значение выводится в консоль.

    Использование итераторов для создания собственных коллекций

    Мы также можем создать собственные коллекции, которые поддерживают итераторы, чтобы использовать их в цикле for. of .

    Для этого нам нужно определить метод Symbol.iterator() в нашей коллекции. Этот метод должен возвращать объект-итератор.

    Рассмотрим пример создания собственной коллекции:

    const myCollection =   items: [1, 2, 3],  [Symbol.iterator]()   let index = 0;  return   next: () =>   if (index  this.items.length)   return  value: this.items[index++], done: false >;  > else   return  done: true >;  >  >  >;  > >;  for (const item of myCollection)   console.log(item); > 

    Здесь мы создаем объект myCollection , который имеет свойство items , содержащее массив элементов. Мы также определяем метод Symbol.iterator() , который возвращает объект-итератор.

    Объект-итератор содержит переменную index , которая указывает на текущий элемент в массиве. Метод next() увеличивает index и возвращает объект со следующим элементом в массиве и свойством done.

    Затем мы используем цикл for. of , чтобы перебрать элементы нашей собственной коллекции.

    Заключение

    Итераторы предоставляют удобный способ получения последовательных значений из коллекций. Они могут быть использованы на разных типах коллекций, включая массивы, строки и объекты.

    Мы также можем использовать итераторы для создания собственных коллекций, которые могут быть перебраны с помощью цикла for. of .

    Итераторы упрощают код и делают его более читабельным, что делает их полезными инструментами в разработке JavaScript.

    Меню категорий

      Загрузка категорий.

    Добро пожаловать в Блог Разработчика Владислава Александровича.

    Ведется медленная, но уверенная разработка функционала сайта.

    Django Core: 0.3.4 / Next.js 1.0 / UPD: 05.06.2023

    Итератор

    Разбираемся, как сделать перебор элементов коллекций в порядке, котором хочется.

    Время чтения: меньше 5 мин

    Открыть/закрыть навигацию по статье

    1. Кратко
    2. Как понять
    3. Зачем это нужно
    4. Встроенные итераторы
    5. Где ещё встречается итератор
      1. Деструктуризация
      2. Array.from()
      3. Спред-оператор
      4. Map, Set

      Кратко

      Скопировать ссылку «Кратко» Скопировано

      Итератор — это объект, который умеет обращаться к элементам коллекции по одному за раз, при этом отслеживая своё текущее положение внутри этой последовательности.

      Иными словами итератор — это такой механизм, который позволяет перемещаться (итерироваться) по элементам коллекции в определённом порядке и делает их доступными.

      Как понять

      Скопировать ссылку «Как понять» Скопировано

      В JavaScript итератор — это объект, который возвращает следующий элемент последовательности, через метод next ( ) . Этот метод возвращает объект с двумя свойствами:

      • value — значение текущего элемента коллекции.
      • done — индикатор, указывающий, есть ли ещё в коллекции значения, доступные для перебора.
       function makeIterator(array)  let nextIndex = 0 return  next: function ()  if (nextIndex < array.length)  const result = nextIndex++ return result > else  return > > >> function makeIterator(array)  let nextIndex = 0 return  next: function ()  if (nextIndex  array.length)  const result =  value: array[nextIndex], done: false > nextIndex++ return result > else  return  done: true > > > > >      

      После создания, объект-итератор может быть явно использован, с помощью вызовов метода next ( ) :

       let iterator = makeIterator(['Hello', 'world'])console.log(iterator.next().value)// 'Hello'console.log(iterator.next().value)// 'world'console.log(iterator.next().done)// true let iterator = makeIterator(['Hello', 'world']) console.log(iterator.next().value) // 'Hello' console.log(iterator.next().value) // 'world' console.log(iterator.next().done) // true      

      Как только метод next ( ) завершает перебор, то возвращается < done : true >. Это является сигналом, что итерирование завершено.

      Зачем это нужно

      Скопировать ссылку «Зачем это нужно» Скопировано

      Практически везде, где нужен перебор, он осуществляется через итераторы. Это включает в себя не только строки, массивы, но и другие структуры данных. В современный JavaScript добавлена новая концепция «итерируемых» (iterable) объектов, например Map , представленный в ES6.

      Это позволяет перебрать итерируемый объект в цикле for . . of :

       for (let value of ['a', 'b', 'c'])  console.log(value) // a // b // c> for (let value of ['a', 'b', 'c'])  console.log(value) // a // b // c >      

      Чтобы быть итерируемым, объект должен реализовать метод @@iterator . Это означает, что он (или один из объектов в цепочке прототипов) должен иметь свойство Symbol . iterator .

      Предположим, у нас есть объект person , который представляет набор данных:

       const person =  name: 'Mark', age: 30, gender: 'male', interests: ['music', 'fishing'],> const person =  name: 'Mark', age: 30, gender: 'male', interests: ['music', 'fishing'], >      

      Чтобы сделать такой объект итерируемым (и позволить for . . of работать с ним), в нём нужно определить Symbol . iterator :

       person[Symbol.iterator] = function ()  const properties = Object.keys(this) let count = 0 return  next()  if (count < properties.length)  const key = properties[count] let result = count++ return result > else  return > >, >> person[Symbol.iterator] = function ()  const properties = Object.keys(this) let count = 0 return  next()  if (count  properties.length)  const key = properties[count] let result =  done: false, value: person[key] > count++ return result > else  return  done: true > > >, > >      

      Убедимся, что объект person действительно итерируется:

       for (let x of person)  console.log(x) // Mark, 30, male, ['music', 'fishing']> for (let x of person)  console.log(x) // Mark, 30, male, ['music', 'fishing'] >      

      Встроенные итераторы

      Скопировать ссылку «Встроенные итераторы» Скопировано

      В некоторых случаях интерфейс итератора вызывается по умолчанию. Такие объекты как String , Array , Map и Set являются итерируемыми, потому что их прототипы содержат Symbol . iterator .

      Где ещё встречается итератор

      Скопировать ссылку «Где ещё встречается итератор» Скопировано

      Деструктуризация

      Скопировать ссылку «Деструктуризация» Скопировано

      При деструктуризации итератор используется для доступа к элементам коллекции:

       const [a, b] = new Set(['a', 'b', 'c'])// a// b const [a, b] = new Set(['a', 'b', 'c']) // a // b      

      Array . from ( )

      Скопировать ссылку «Array.from()» Скопировано

      Array . from ( ) позволяет конвертировать итерируемый объект в массив:

       const arr = Array.from(new Set(['a', 'b', 'c']))// ['a', 'b', 'c'] const arr = Array.from(new Set(['a', 'b', 'c'])) // ['a', 'b', 'c']      

      Спред-оператор

      Скопировать ссылку «Спред-оператор» Скопировано

      Спред-оператор (spread) также вызывает интерфейс итератора по умолчанию:

       const arr = [. new Set(['a', 'b', 'c'])]// ['a', 'b', 'c'] const arr = [. new Set(['a', 'b', 'c'])] // ['a', 'b', 'c']      

      Map , Set

      Скопировать ссылку «Map, Set» Скопировано

      Конструкторы Map и Set преобразуют итерируемые значения в соответственно Map и Set :

       const map = new Map([ ['uno', 'one'], ['dos', 'two'],])map.get('uno')// onemap.get('dos')// two const set = new Set(['red', 'green', 'blue'])set.has('red')// trueset.has('yellow')// false const map = new Map([ ['uno', 'one'], ['dos', 'two'], ]) map.get('uno') // one map.get('dos') // two const set = new Set(['red', 'green', 'blue']) set.has('red') // true set.has('yellow') // false      

      Коллекции и итераторы

      Итераторы применяются для организации последовательного доступа к элементам наборов данных — массивам, объектам Set, Map, строкам и т.д..Так, благодаря итераторам мы можем перебрать набор данных с помощью цикла for-of :

      const people = [«Tom», «Bob», «Sam»]; for(const person of people)

      В цикле for-of справа от оператора of указывается набор данных или перебираемый объект, из которого в цикле мы можем получить отдельные элементы. Но эта возможность перебора некоторого объекта, как, например, массива в примере выше, реализуются благодаря тому, что эти объекты применяют итераторы. Рассмотрим подробнее, что представляют итераторы и как можно создать свой итератор.

      Итераторы предоставляют метод next() , который возвращает объект с двумя свойствами: value и done

      Свойство value хранит собственно значение текущего перебираемого элемента. А свойство done указывает, есть ли еще в коллекции объекты, доступные для перебора. Если в наборе еще есть элементы, то свойство done равно false Если же доступных элементов для перебора больше нет, то это свойство равно true , а метод next() возвращает объект

      Некоторые типы в JavaScript имеют методы, которые возвращают итераторы. Например, метод entries() типа Array (то есть массивов):

      const people = ["Tom", "Bob", "Sam"]; const items = people.entries(); console.log(items.next());

      Здесь метод next() возвратит следующий объект на консоль:

       done: false value: Array(2) 0: 0 1: "Tom" length: 2 __proto__: Array(0) __proto__: Object

      Здесь мы видим, что свойство done имеет значение false , так как мы перебрали только один элемент в множестве, и там еще есть два элемента.

      Свойство value представляет массив из двух значений. Первое значение представляет ключ или индекс элемента массива, а второй элемент — значение по этому индексу. имеет ключ и значение.

      Соответственно мы можем организовать и перебор всей коллекции:

      const people = [«Tom», «Bob», «Sam»]; const items = people.entries(); // получаем итератор while(!(item = items.next()).done)

      Здесь в цикле while из метода next() итератора получаем текущий объект в переменную item : item = items.next()

      И смотрим на ее свойство done — если оно равно false (то есть в наборе еще есть элементы), то продолжаем цикл

      while(!(item = items.next()).done)
      

      В цикле обращаемся к свойству value полученного объекта

      console.log(item.value);
      [0, "Tom"] [1, "Bob"] [2, "Sam"]

      Поскольку каждый возвращаемый элемент представляет массив, где первый элемент - индекс в массиве, а второй - само значение, то, обратившись ко второму элементу, мы можем получить, значение:

      while(!(item = items.next()).done)

      Но в этом нет смысла, поскольку все коллекции, которые возвращают итераторы, поддерживают перебор с помощью цикла for. of , который как раз и использует итератор для получения элементов.

      Разные объекты могут иметь свою собственную реализацию итератора. И при необходимости мы можем определить объект со своим итератором. Применение итераторов предоставляет нам способ создать объект, который будет вести себя как коллекция элементов

      Для создания перебираемого объекта нам надо определить в объекта метод [Symbol.iterator]() . Этот метод собственно и будет представлять итератор:

      const iterable = < [Symbol.iterator]() < return < next() < // если еще есть элементы return < value: . done: false >; // если больше нет элементов return < value: undefined, done: true >; > >; > >;

      Метод [Symbol.iterator]() возвращает объект, который имеет метод next() . Этот метод возвращает объект с двумя свойствами value и done .

      Если в нашем объекте есть элементы, то свойство value содержит собственно значение элемента, а свойство done равно false .

      Если доступных элементов больше нет, то свойство done равно true .

      Например, реализуем простейший объект с итератором, который возвращает некоторый набор чисел:

      const iterable = < [Symbol.iterator]() < return < current: 1, end: 3, next() < if (this.current ; > return < done: true >; > >; > >;

      Здесь итератор фактически возвращает числе от 1 до 3. Для отслеживания текущего элемента в объекте, который возвращается методом , определены два свойства:

      current: 1, end: 3,

      Свойство current собственно хранит значение текущего элемента. А свойство end задает предел. То есть в данном случае итератор возвращает числа от 1 до 3.

      В методе next() , если текущее значение меньше или равно редельному значению, возвращаем объект

      return < value: this.current++, done: false >;

      Инкремент this.current++ приведет к тому, что при следующем вызове метода next значение current будет на единицу больше.

      Если достигнут предел, то возвращаем объект

      return < done: true >;

      Это будет указывать, что объектов больше нет.

      Получим из итератора возвращаемые им элементы:

      const myIterator = iterable[Symbol.iterator](); // получаем итератор console.log(myIterator.next()); // console.log(myIterator.next()); // console.log(myIterator.next()); // console.log(myIterator.next()); //

      Здесь сначала получаем итератор в константу myIterator . Затем при обращении к ее методу next() последовательно получаем все элементы. При четвертом вызове метода next условный перебор элементов в итераторе закончен, и метод возвращает объект .

      Однако если мы хотим перебрать наш объект и получить из него его элементы, то нам не надо обращаться к методу next() . Поскольку объект iterable реализует итератор, то его можно перебрать с помощью цикла for-of :

      const iterable = < [Symbol.iterator]() < return < current: 1, end: 3, next() < if (this.current ; > return < done: true >; > >; > >; for (const value of iterable)

      1 2 3

      Цикл for-of автоматически обращается к методу next() и извлекает значение.

      Рассмотрим еще один пример:

      const team = < [Symbol.iterator]() < return < index: 0, members: ["Tom", "Alice", "Kate"], next() < if (this.index < this.members.length) < return < value: this.members[this.index++], done: false >; > return < done: true >; > >; > >; for (const member of team)

      Здесь объект team представляет условную команду и применяет итератор, который возвращает элементы из массива members - участники команды. Для отслеживания индекса текущего возвращаемого элемента из массива members определено свойство index , которое инкрементируется при каждом вызове метода next.

      Реализация итератора позволяет рассматривать объект team как условную коллекцию, которую можно перебрать в цикле for-of . Консольный вывод программы:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *