Итерировать объект как массив
Первое, что приходит мне на ум, когда я слышу об итераторе, — это массив. А почему бы и нет, массивы очень активно используются в различных языках программирования. Но пока я изучал концепцию итераторов в JS, я понял, что массивы представляют собой лишь часть того, что такое итератор и что он может делать. Для JS-программиста знание того, что такое итератор и как его использовать, может оказаться очень полезным.
Семантика
- Итератор — это то, что реализует протокол итератора.
- Протокол итератора — это стандартный способ создания последовательности значений и возможного возврата значения после завершения создания последовательности.
- Протокол указывает, что итератор должен реализовать метод next. При вызове метода next он возвращает объект со свойствами done и value. Для параметра done устанавливается значение false, если итерация продолжается, и значение true, когда итерация завершена. значение — это значение, созданное итератором. Когда done имеет значение true, значение может быть установлено как неопределенное.
- Тип данных является итерируемым, если для него определен метод Symbol.iterator и он удовлетворяет протоколу итератора, как указано в пункте 3.
Примеры
Давайте посмотрим на несколько встроенных итераторов:
- Множество
const a = [1, 2, 3]; typeof a; // object Array.isArray(a); // true // Check if the array has Symbol.iterator method defined const arrayIterator = a[Symbol.iterator](); // Does the iterator have next method available on it arrayIterator.next(); // {value: 1, done: false} arrayIterator.next(); // {value: 2, done: false} arrayIterator.next(); // {value: 3, done: false} arrayIterator.next(); // {value: undefined, done: true} // Array is a type of Iterator
2. Список узлов
Другой пример итератора. Откройте консоль на любой веб-странице приличного размера и выполните приведенные ниже команды.
const a = document.querySelectorAll('div'); typeof a; // object Array.isArray(a); // false. Its a NodeList // Check if the array has Symbol.iterator method defined const nodeListIterator = a[Symbol.iterator](); // Does the iterator have next method available on it nodeListIterator.next(); // {value: div.butterBar, done: false} // NodeList is a type of Iterator
Как вы можете видеть выше, объект не является массивом, но все же является итератором.
3. Строка
Строка также является итератором. JS делает волшебство в фоновом режиме, чтобы заставить примитив вести себя как итератор.
const a = 'hello'; typeof a; // string // Check if the string has Symbol.iterator method defined const stringIterator = a[Symbol.iterator](); // Does the iterator have next method available on it stringIterator.next(); // {value: 'h', done: false} // String is a type of Iterator
Можете ли вы создать итератор самостоятельно? Абсолютно!!
Просто создайте объект, удовлетворяющий протоколу итератора, и вы получите все тонкости итератора.
// Regular object that we want to iterate between from and to const a = { from: 1, to: 5 }; // Define a function that satisfies the iterator protocol we // discussed above const iteratorProtocol = function () { return { current: this.from, last: this.to, next: function() { if (this.current > this.last) { return { done: true, value: undefined }; } return { done: false, value: this.current++ } } }; } // Make the object iterable a[Symbol.iterator] = iteratorProtocol; const iterator = a[Symbol.iterator](); iterator.next(); // { done: false, value: 1 } // Voila! We just created an iterator
Прелести итератора
- Вы можете использовать for…of и получить доступ к значениям итерируемого объекта.
- Вы можете использовать синтаксис распространения и преобразовать любую итерацию в массив.
- Заимствование методов из других итераций становится очень простым. Массив в Javascript имеет множество полезных методов, которые можно легко позаимствовать. Отлично!
const a = 'hello'; for (val of a) { console.log(val); } // 'h', 'e', 'l', 'l', 'o' const b = [...a]; // ['h', 'e', 'l', 'l', 'o'] Array.prototype.filter.call(a, item => item === 'l'); // ['l', 'l']; // Wow, I just filtered a string.
Сводка
Означает ли это, что я начинаю реализовывать объекты с протоколами итераторов везде, где это необходимо? Ну, как и большинство вещей в программировании, это зависит. Важно писать код, который ваши коллеги-разработчики смогут легко понять и корректно изменить в ваше отсутствие. Если вы чувствуете, что ваши коллеги-разработчики не смогут следовать этому синтаксису, лучше реализовать служебную функцию, которая отвечает вашим требованиям.
Итераторы — это гораздо больше, чем то, что было рассмотрено выше. Такие темы, как генераторы, асинхронные итераторы, протоколы асинхронных итераторов вообще не были освещены. Им нужна отдельная запись в блоге, но, надеюсь, эта запись в блоге вызвала некоторое любопытство и проложила путь для более глубоких знаний в будущем.