Generator
Обычные функции возвращают только одно значение (или не возвращают никакого значения).
Генераторы, с другой стороны, могут возвращать («выдавать») несколько значений, одно за другим, по мере необходимости. Их можно сочетать сiterable
Прекрасно работает вместе, упрощая создание потоков данных.
Функция генератора
Для создания генератора нам понадобится специальная синтаксическая конструкция:function*
, так называемая "генераторная функция".
Это выглядит так:
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
Генераторные функции ведут себя иначе, чем обычные функции. Когда такая функция вызывается, она не запускает свой код. Вместо этого возвращается специальный объект, называемый «объект-генератор», для управления потоком выполнения.
Давайте посмотрим на пример:
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
// "generator function" 创建了一个 "generator object"
let generator = generateSequence();
alert(generator); // [object Generator]
До сих пор в приведенном выше кодетело функцииКод еще не начал выполняться:
Основной метод генератораnext()
. когда звонятnext()
метод), он возобновляет операцию, показанную выше, выполняя до самого последнегоyield <value>
утверждение (value
можно не указывать, по умолчаниюundefined
). Затем выполнение функции приостанавливается, и полученное значение возвращается внешнему коду.next()
Результатом всегда является объект с двумя свойствами:
-
value
: полученное значение. -
done
: если функция генератора завершила выполнениеtrue
, иначеfalse
.
Например, мы можем создать генератор и получить его первое полученное значение:
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
let generator = generateSequence();
let one = generator.next();
alert(JSON.stringify(one)); // {value: 1, done: false}
Пока у нас есть только первое значение, а теперь выполнение функции находится на второй строке:
позвоним еще разgenerator.next()
. Код возобновляет выполнение и возвращается к следующемуyield
Значение :
let two = generator.next();
alert(JSON.stringify(two)); // {value: 2, done: false}
Если мы позвоним в третий разgenerator.next()
, код будет выполняться до тех пор, покаreturn
оператор, выполнение этой функции завершается на этом этапе:
let three = generator.next();
alert(JSON.stringify(three)); // {value: 3, done: true}
Теперь выполнение генератора завершено. мы проходимdone:true
можно увидеть это иvalue:3
обработка является конечным результатом.
опять такиgenerator.next()
Делать новый звонок больше не имеет смысла. Если мы сделаем это, он вернет тот же объект:{done: true}
.
function* f(…)
илиfunction *f(…)
?Оба синтаксиса верны.
Но обычно предпочитают первый синтаксис, потому что звездочка
*
указывает, что это функция-генератор, которая описывает тип функции, а не имя, поэтому*
должен иfunction
Ключевые слова слипаются.
Генератор повторяемый
когда ты видишьnext()
как вы уже догадались, генераторы являются итерируемыми. (Аннотация:next()
это необходимый метод итератора)
мы можем использоватьfor..of
Перебрать все его значения:
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
let generator = generateSequence();
for(let value of generator) {
alert(value); // 1,然后是 2
}
for..of
Орфография выглядит лучше, чем.next().value
Более элегантный?
... но обратите внимание: в приведенном выше примере сначала будет показано1
,После 2
, а потом ушел. это не показывает3
!
Это потому, что когдаdone: true
час,for..of
Цикл игнорирует последнийvalue
. Поэтому, если мы хотим пройтиfor..of
Чтобы просмотреть все результаты, мы должны использоватьyield
возвращает их:
function* generateSequence() {
yield 1;
yield 2;
yield 3;
}
let generator = generateSequence();
for(let value of generator) {
alert(value); // 1,然后是 2,然后是 3
}
Поскольку генераторы являются итерируемыми, мы можем использовать все связанные функции итератора, например: синтаксис распространения...
:
function* generateSequence() {
yield 1;
yield 2;
yield 3;
}
let sequence = [0, ...generateSequence()];
alert(sequence); // 0, 1, 2, 3
В приведенном выше коде...generateSequence()
Преобразуйте объект итерируемого генератора в массив
Итерация с генераторами
В объекте Iterable мы можем создать итерируемыйrange
объект, который возвращаетfrom..to
ценность .
let range = {
from: 1,
to: 5,
// for..of range 在一开始就调用一次这个方法
[Symbol.iterator]() {
// ...它返回 iterator object:
// 后续的操作中,for..of 将只针对这个对象,并使用 next() 向它请求下一个值
return {
current: this.from,
last: this.to,
// for..of 循环在每次迭代时都会调用 next()
next() {
// 它应该以对象 {done:.., value :...} 的形式返回值
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
}
};
// 迭代整个 range 对象,返回从 `range.from` 到 `range.to` 范围的所有数字
alert([...range]); // 1,2,3,4,5
Мы можем сделать это, предоставив функцию генератора какSymbol.iterator
, чтобы выполнить итерацию с помощью генератора:
Ниже приведен идентичныйrange
, но намного компактнее:
let range = {
from: 1,
to: 5,
*[Symbol.iterator]() { // [Symbol.iterator]: function*() 的简写形式
for(let value = this.from; value <= this.to; value++) {
yield value;
}
}
};
alert( [...range] ); // 1,2,3,4,5
Код работает правильно, потому чтоrange[Symbol.iterator]()
Теперь возвращает генератор, и метод генератора точноfor..of
Ожидал:
- она имеет
.next()
метод - Это начинается с
{value: ..., done: true/false}
возвращаемое значение в виде
Конечно, это не случайно. Генераторы были добавлены в язык JavaScript с учетом итераторов, чтобы упростить реализацию итераторов.
Вариант с генератором лучше оригиналаrange
Код итерации намного чище и сохраняет ту же функциональность.
Генераторы всегда могут давать значения
В приведенном выше примере мы сгенерировали конечную последовательность, но мы также могли бы создать генератор, который генерирует бесконечную последовательность, которая все время выдает значения. Например, неупорядоченная последовательность псевдослучайных чисел.
В этом случае он должен быть в генераторе.
for..of
добавить петлюbreak
(илиreturn
). В противном случае цикл будет повторяться вечно и зависать.
Детали перепечатки:
Перепечатанный веб-сайт: Javascript.info
Адрес перепечатки:zh.javascript.info/generators