Как определить генератор ES6

Скажем, у меня есть функция генератора, как это:

var g = function*() {
  yield 1;
  yield 2;
  yield 3;
};

var gen = g();

Как я могу программно сказать, что g является функцией генератора, или что gen такое итератор?

Это похоже на одну возможность:

g.constructor.name === 'GeneratorFunction'

Есть ли способ лучше?

Обновление: я закончил тем, что использовал подход, подобный ответу Эрика, но используя eval сначала определить, поддерживаются ли генераторы на целевой платформе. Вот реализация:

var GeneratorConstructor = (function() {
  try {
    var generator;
    return eval('generator = function*() { yield 1; };').constructor;

  } catch (e) {
    // If the above throws a SyntaxError, that means generators aren't
    // supported on the current platform, which means isGenerator should
    // always return false. So we'll return an anonymous function here, so
    // that instanceof checks will always return false.
    return function() {};
  }
}());

/**
 * Checks whether a function is an ES6 Harmony generator.
 *
 * @private
 * @param {Function} fn
 * @returns {boolean}
 */
function isGenerator(fn) {
  return fn instanceof GeneratorConstructor;
}

3 ответа

Решение

Объединяя ваше решение с другими решениями, это избавляет от необходимости глобального GeneratorFunction:

g instanceof (function*() {}).constructor

Следующее изображение из текущего проекта ES6 довольно информативно для демонстрации взаимосвязей между функциями генератора и другими объектами:

Рисунок 2 (информативный) - связь между объектомами генератора

Похоже, вы должны быть в состоянии использовать g instanceof GeneratorFunction если у вас есть глобальная ссылка на GeneratorFunctionВ противном случае я думаю, что ваш нынешний подход просто отлично.

Вот как вы можете получить ссылку на GeneratorFunction и другие связанные объекты, заимствованные из файла модульного тестирования V8:

function* g() { yield 1; }
var GeneratorFunctionPrototype = Object.getPrototypeOf(g);
var GeneratorFunction = GeneratorFunctionPrototype.constructor;
var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype;

Чтобы сказать, если что-то является генератором:

const g = (function*() {yield 1;});
const GeneratorFunctionPrototype = Object.getPrototypeOf(g);

function isGenerator(thing) {
  return typeof thing === 'object' && thing.constructor === GeneratorFunctionPrototype;
}

В оригинальном вопросе это ответы isGenerator(gen) правда. В то время как g является функцией генератора. Когда вы вызываете функцию генератора, вы создаете генератор.

Но в большинстве случаев более важно спросить, является ли эта вещь итератором:

function isIterator(thing) {
  // If thing has a Symbol.iterator
  return typeof thing === 'object' && thing[Symbol.iterator];
}

Использование:

function *Pseq(list, repeats=1) {
  for (let i = 0; i < repeats; i++) {
    for (let value of list) {
      if (isIterator(value)) {
        yield *value;
      } else {
        yield value;        
      }
    }    
  }
}

let q = Pseq([1, 2, 3, Pseq([10, 20, 30], 2), 4], 2);

for (let v of q) {
  console.log(v);
}

1 2 3 10 20 30 10 20 30 4 1 2 3 4

Это повторение последовательности. Если в эту последовательность встроен итератор, который использует делегирование yield для итерации, перед тем как продолжить последовательность. Генераторы - не единственное, что создает полезные итераторы, которые вы можете использовать.

Другие вопросы по тегам