This page was translated from English by the community.Learn more and join the MDN Web Docs community.
The Iterator protocol
Одно из нововведений стандарта ECMAScript 2015 - протоколы перебора, которые могут реализованы любым объектом, соблюдая при этом определённые правила.
In this article
- Протоколы перебора
- Примеры использования протокола "итератора"
- Встроенная итерируемость
- Итерируемость определённая пользователем
- Builtin APIs need iterables
- Синтаксис предполагающий итерируемость
- Non-well-formed iterables
- Объект-генератор является итератором или итерируемым
- Примеры
- Спецификации
- Смотрите также
Протоколы перебора
Протоколы перебора включаютthe "iterable" protocol иthe "iterator" protocol.
Протокол "Итерируемый"
Протокол "Итерируемый" позволяет JavaScript объектам определять или настраивать поведение перебора, например, то какие значения перебираются в конструкцииfor..of. Некоторые встроенные типы, такие какArray илиMap, имеют поведение перебора по умолчанию, в то время как другие типы (такие какObject) его не имеют
Для того, чтобы объект былитерируемым, в нем должен быть реализован метод@@iterator, т.е. этот объект (или любой из объектов из егоprototype chain) должен иметь свойство с именемSymbol.iterator:
| Свойство | Значение |
|---|---|
[Symbol.iterator] | Функция без аргументов, возвращающая объект, соответствующийiterator protocol. |
Всякий раз, когда объект подлежит перебору (например, когда в коде встречается циклfor..of), вызывается его метод@@iterator без аргументов, и возвращаемыйiterator используется для получения перебираемых значений.
Протокол "Итератор"
Протокол "Итератор" определяет стандартный способ получения последовательности значений (конечной или бесконечной).
Объект является итератором, если в нем определён методnext() , реализующий следующую логику:
| Свойство | Значение |
|---|---|
next | Функция без аргументов, возвращающая объект с двумя свойствами:
|
Некоторые итераторы, в свою очередь, итерабельны:
var someArray = [1, 5, 7];var someArrayEntries = someArray.entries();someArrayEntries.toString(); // "[object Array Iterator]"someArrayEntries === someArrayEntries[Symbol.iterator](); // trueПримеры использования протокола "итератора"
String является примером встроенного итерабельного объекта:
var someString = "hi";typeof someString[Symbol.iterator]; // "function"По умолчанию итератор строки возвращает символы строки друг за другом:
var iterator = someString[Symbol.iterator]();iterator + ""; // "[object String Iterator]"iterator.next(); // { value: "h", done: false }iterator.next(); // { value: "i", done: false }iterator.next(); // { value: undefined, done: true }Некоторые встроенные конструкции языка, например,spread operator, используют в своей внутренней реализации тот же протокол итерации:
[...someString]; // ["h", "i"]Поведение итератора можно переопределить применив собственный@@iterator:
var someString = new String("hi"); // need to construct a String object explicitly to avoid auto-boxingsomeString[Symbol.iterator] = function () { return { // this is the iterator object, returning a single element, the string "bye" next: function () { if (this._first) { this._first = false; return { value: "bye", done: false }; } else { return { done: true }; } }, _first: true, };};Notice how redefining@@iterator affects the behavior of built-in constructs, that use the iteration protocol:
[...someString]; // ["bye"]someString + ""; // "hi"Встроенная итерируемость
String,Array,TypedArray,Map иSet итерируемы, так как их прототипы содержат@@iterator метод, аObject нет, так как прототипObject не содержит метода@@iterator
Итерируемость определённая пользователем
Мы можем создать итерируемый объект сами:
var myIterable = {};myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3;};[...myIterable]; // [1, 2, 3]Builtin APIs need iterables
Map([iterable]),WeakMap([iterable]),Set([iterable]) andWeakSet([iterable]):
var myObj = {};new Map([ [1, "a"], [2, "b"], [3, "c"],]).get(2); // "b"new WeakMap([ [{}, "a"], [myObj, "b"], [{}, "c"],]).get(myObj); // "b"new Set([1, 2, 3]).has(3); // truenew Set("123").has("2"); // truenew WeakSet( (function* () { yield {}; yield myObj; yield {}; })(),).has(myObj); // trueandPromise.all(iterable),Promise.race(iterable),Array.from()
Синтаксис предполагающий итерируемость
for-of,spread, yield*, destructing - использование данного синтаксиса возможно только если типы данных, к которым он применяется, итерируемы:
for (let value of ["a", "b", "c"]) { console.log(value);}// "a"// "b"// "c"[..."abc"]; // ["a", "b", "c"]function* gen() { yield* ["a", "b", "c"];}gen().next()[(a, b, c)] = // { value:"a", done:false } new Set(["a", "b", "c"]);a; // "a"Non-well-formed iterables
If an iterable's@@iterator method doesn't return an iterator object, then it's a non-well-formed iterable, using it as such is likely to result in runtime exceptions or buggy behavior:
var nonWellFormedIterable = {}nonWellFormedIterable[Symbol.iterator] = () => 1[...nonWellFormedIterable] // TypeError: [] is not a functionОбъект-генератор является итератором или итерируемым
И тем и другим
var aGeneratorObject = function*(){ yield 1; yield 2; yield 3;}()typeof aGeneratorObject.next// "function", because it has a next method, so it's an iteratortypeof aGeneratorObject[Symbol.iterator]// "function", because it has an @@iterator method, so it's an iterableaGeneratorObject[Symbol.iterator]() === aGeneratorObject// true, because its @@iterator method return its self (an iterator), so it's an well-formed iterable[...aGeneratorObject]// [1, 2, 3]Примеры
>Простой итератор
function makeIterator(array) { var nextIndex = 0; return { next: function () { return nextIndex < array.length ? { value: array[nextIndex++], done: false } : { done: true }; }, };}var it = makeIterator(["yo", "ya"]);console.log(it.next().value); // 'yo'console.log(it.next().value); // 'ya'console.log(it.next().done); // trueБесконечный итератор
function idMaker() { var index = 0; return { next: function () { return { value: index++, done: false }; }, };}var it = idMaker();console.log(it.next().value); // '0'console.log(it.next().value); // '1'console.log(it.next().value); // '2'// ...С генератором
function* makeSimpleGenerator(array) { var nextIndex = 0; while (nextIndex < array.length) { yield array[nextIndex++]; }}var gen = makeSimpleGenerator(["yo", "ya"]);console.log(gen.next().value); // 'yo'console.log(gen.next().value); // 'ya'console.log(gen.next().done); // truefunction* idMaker() { var index = 0; while (true) yield index++;}var gen = idMaker();console.log(gen.next().value); // '0'console.log(gen.next().value); // '1'console.log(gen.next().value); // '2'// ...Спецификации
| Specification |
|---|
| ECMAScript® 2026 Language Specification> # sec-iteration> |
Смотрите также
- Дополнительную информацию о генераторах ES 2015 смотрина отдельной странице.