Генераторная функция Js (1)

внешний интерфейс JavaScript
Генераторная функция Js (1)

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] 

До сих пор в приведенном выше кодетело функцииКод еще не начал выполняться:

1640572534(1).jpg

Основной метод генератора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}

Пока у нас есть только первое значение, а теперь выполнение функции находится на второй строке:

企业微信截图_16405726069730.pngпозвоним еще разgenerator.next(). Код возобновляет выполнение и возвращается к следующемуyieldЗначение :

let two = generator.next();

alert(JSON.stringify(two)); // {value: 2, done: false}

1640572655(1).jpgЕсли мы позвоним в третий разgenerator.next(), код будет выполняться до тех пор, покаreturnоператор, выполнение этой функции завершается на этом этапе:

let three = generator.next();

alert(JSON.stringify(three)); // {value: 3, done: true}

image.pngТеперь выполнение генератора завершено. мы проходим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