- Операторы
- Управляющие инструкции
- JS Объекты
- Array
- Boolean
- Date
- Error
- Function
- Global
- JSON
- Math
- Number
- Object
- RegExp
- String
- Unicode
- Symbol
- Итераторы и генераторы
- Map и WeakMap
- Set и WeakSet
- Локализация
- браузер BOM
- HTML DOM
- События
- HTML Объекты
- Промисы, async/await
- Сетевые запросы
- Бинарные данные и файлы
- Модули
- Классы
- Разное
Итераторы и генераторы
Обработка каждого элемента коллекции является весьма распространённой операцией. JavaScript предоставляет несколько способов перебора коллекции, от простого цикла for до map() и filter(). Итераторы и генераторы внедряют концепцию перебора непосредственно в ядро языка и обеспечивают механизм настройки поведения цикла for...of.
Итераторы
Объект является итератором (перебираемым) , если он умеет обращаться к элементам коллекции по одному за раз, при этом отслеживая своё текущее положение внутри этой последовательности. В JavaScript итератор - это объект, который предоставляет метод next(), возвращающий следующий элемент последовательности. Этот метод возвращает объект с двумя свойствами:done и value:
- done
- Принимает значение
trueесли итератор достиг конца итерируемой последовательности. В этом случае свойствоvalueможет определять возвращаемое значение итератора. - Принимает значение
falseесли итератор может генерировать следующее значение последовательности. Это эквивалентно не указанному done.
- Принимает значение
- value
- любое JavaScript значение, возвращаемое итератором. Может быть опущено, если done имеет значение true.
Перебираемые (или итерируемые) объекты – это концепция, которая позволяет использовать любой объект в цикле for..of.
Конечно же, сами массивы (Array) являются перебираемыми объектами. Но есть и много других встроенных перебираемых объектов: String, Map, Set, arguments. ...
Если объект не является массивом, но представляет собой коллекцию каких-то элементов, то удобно использовать цикл for..of для их перебора
Чтобы сделать объект итерируемым (и позволить for..of работать с ним), нужно добавить в объект метод с именем Symbol.iterator (специальный встроенный Symbol, созданный как раз для этого).
- Когда цикл
for..ofзапускается, он вызывает этот метод один раз (или выдаёт ошибку, если метод не найден). Этот метод должен вернуть итератор – объект с методомnext. - Дальше
for..ofработает только с этим возвращённым объектом. - Когда
for..ofхочет получить следующее значение, он вызывает методnext()этого объекта. - Результат вызова
next()должен иметь вид{done: Boolean, value: any}, гдеdone=trueозначает, что итерация закончена, в противном случаеvalueсодержит очередное значение.
Пример
let range = {
from: 1,
to: 5
};
// 1. вызов for..of сначала вызывает эту функцию
range[Symbol.iterator] = function() {
// ...она возвращает объект итератора:
// 2. Далее, for..of работает только с этим итератором, запрашивая у него новые значения
return {
current: this.from,
last: this.to,
// 3. next() вызывается на каждой итерации цикла for..of
next() {
// 4. он должен вернуть значение в виде объекта {done:.., value :...}
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
};
let s='';
for (let num of range) { s += num+' '; }
Alert(s); // 1 затем 2 3 4 5
Итерируемые объекты и псевдомассивы
Есть два официальных термина, которые очень похожи, но в то же время сильно различаются. Поэтому убедитесь, что вы как следует поняли их, чтобы избежать путаницы.
- Итерируемые объекты – это объекты, которые реализуют метод
Symbol.iterator, как было описано выше. - Псевдомассивы – это объекты, у которых есть индексы и свойство
length, то есть, они выглядят как массивы.
При использовании JavaScript в браузере или других окружениях мы можем встретить объекты, которые являются итерируемыми или псевдомассивами, или и тем, и другим.
Например, строки итерируемы (для них работает for..of) и являются псевдомассивами (они индексированы и есть length).
Но итерируемый объект может не быть псевдомассивом. И наоборот: псевдомассив может не быть итерируемым.
Генераторы
Генераторы -- новый вид функций в современном JavaScript. Они отличаются от обычных тем, что могут приостанавливать своё выполнение, возвращать промежуточный результат и далее возобновлять его позже, в произвольный момент времени.
Генераторы - это специальный тип функции, который работает как фабрика итераторов. Функция становится генератором, если содержит один или более yield операторов и использует синтаксическую конструкцию: function* (функция со звёздочкой). Её называют "функция-генератор" (generator function).yield
Ключевое слово yield используется для остановки и возобновления функций-генераторов (function*)..Синтаксис
[rv] = yield [[выражение]];
- выражение
- Возвращаемое выражение. Если не указано, то возвращается значение
undefined. - rv
- Возвращает необязательное значение, которое передаётся в next() генератора, чтобы возобновить его выполнение.
Описание
Ключевое слово yield вызывает остановку функции-генератора и возвращает текущее значение выражения, указанного после ключевого слова yield. Его можно рассматривать как аналог ключевого слова return в функции-генераторе.
На самом деле ключевое слово yield возвращает объект с двумя параметрами, value и done. При этом, value является результатом вычисления выражения после yield, а done указывает, была ли завершена функция-генератор.
Во время остановки на операторе yield, выполнение кода в функции-генераторе не возобновится, пока не будет вызван метод next() возвращаемого функцией объекта-генератора. Это предоставляет непосредственный контроль за выполнением генератора и возвратом его значений.
Примеры
Следующий фрагмент кода содержит определение функции-генератора и вспомогательной функции:
function* foo(){
var index = 0;
while(index <= 2) // при достижении 2, done в yield станет true, а value undefined;
yield index++;
}
// После того как тело функции-генератора определено, оно может использоваться для получения итератора:
var iterator = foo();
let a = iterator.next();
let s = '\n' + a.value + ', '+a.done; // { value:0, done:false }
a = iterator.next();
s += '\n' + a.value + ', '+a.done; // { value:1, done:false }
a = iterator.next();
s += '\n' + a.value + ', '+a.done; // { value:2, done:false }
a = iterator.next();
s += '\n' + a.value + ', '+a.done; // { value:undefined, done:true }
Alert(s);
function*
function* (ключевое слово function со звёздочкой) определяет функцию-генератор.
Синтаксис
function* name([param[, param[, ... param]]]) { statements }
- name
- Имя функции.
- param
- Именованные аргументы функции (параметры). Функция-генератор может иметь 255 аргументов..
- statements
- Инструкции составляющие тело функции.. .
Описание
Генераторы являются функциями с возможностью выхода и последующего входа. Их контекст исполнения (значения переменных) сохраняется при последующих входах.
Когда вызывается функция-генератор, её тело исполняется не сразу; вместо этого возвращается объект-итератор. При вызове метода next() итератора тело функции-генератора исполняется до первого встреченного оператора yield, который определяет возвращаемое значение или делегирует дальнейшее выполнение другому генератору при помощи yield* anotherGenerator(). Метод next() возвращает объект со свойством value, содержащим отданное значение, и свойством done, которое указывает, что генератор уже отдал своё последнее значение. Вызов метода next() с аргументом прекращает выполнение функции-генератора, и заменяет инструкцию yield на которой было приостановлено выполнение на аргумент переданный в next().
Примеры
Простой пример
function* idMaker() {
var index = 0;
while (index < 3)
yield index++;
}
var gen = idMaker();
Alert( `
${ gen.next().value }
${ gen.next().value }
${ gen.next().value }
${ gen.next().value }
`);
Пример с yield*
function* anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function* generator(i) {
yield i;
yield* anotherGenerator(i);
yield i + 10;
}
var gen = generator(10);
Alert( `
${ gen.next().value }
${ gen.next().value }
${ gen.next().value }
${ gen.next().value }
${ gen.next().value }
`);
Передача аргументов в генератор
function* logGenerator() {
Alert(yield);
Alert(yield);
Alert(yield);
}
var gen = logGenerator();
// первый вызов next выполняется от начала функции
// и до первого оператора yield
gen.next();
gen.next('pretzel'); // pretzel
gen.next('california'); // california
gen.next('mayonnaise'); // mayonnaise
Инструкция return в генераторе
function* yieldAndReturn() {
yield "Y";
return "R";
yield "unreachable";
}
var gen = yieldAndReturn();
let a = gen.next();
let s = '\n' + a.value + ', '+a.done; // { value: "Y", done: false }
a = gen.next();
s += '\n' + a.value + ', '+a.done; // { value: "R", done: true }
a = gen.next();
s += '\n' + a.value + ', '+a.done; // { value: undefined, done: true }
Alert(s);