最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to identify an ES6 generator - Stack Overflow

programmeradmin0浏览0评论

Say I've got a generator function like this:

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

var gen = g();

How can I tell programmatically that g is a generator function, or that gen is an iterator?

This seems like one possibility:

g.constructor.name === 'GeneratorFunction'

Is there a better way?

Update: I ended up taking an approach similar to Eric's answer, but using eval to first determine whether generators are supported on the target platform in the first place. Here is the implementation:

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;
}

Say I've got a generator function like this:

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

var gen = g();

How can I tell programmatically that g is a generator function, or that gen is an iterator?

This seems like one possibility:

g.constructor.name === 'GeneratorFunction'

Is there a better way?

Update: I ended up taking an approach similar to Eric's answer, but using eval to first determine whether generators are supported on the target platform in the first place. Here is the implementation:

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;
}
Share Improve this question edited May 23, 2017 at 12:30 CommunityBot 11 silver badge asked Feb 4, 2014 at 17:30 Dan TaoDan Tao 128k57 gold badges308 silver badges450 bronze badges 2
  • Or you could be more permissive with typeof gen.next === "function" && typeof gen.throw === "function" – Alex K. Commented Feb 4, 2014 at 17:51
  • This will only work if fn is actually created with GeneratorFunction right? Is there a way to determine if an object in general was created from a generator? In firefox gen['@@iterator'] seems to be defined but I can't figure it out for node. I'm surprised iterators don't derive from Iterator so that gen instanceof Iterator works. – Nick Sotiros Commented May 9, 2014 at 15:59
Add a ment  | 

3 Answers 3

Reset to default 5

Combining your solution with other solutions, this avoids the need for the global GeneratorFunction:

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

The following image from the current ES6 draft is pretty informative for showing the relationships between generator functions and other objects:

So it looks like you should be able to use g instanceof GeneratorFunction if you have a global reference to GeneratorFunction, otherwise I think your current approach is just fine.

Here is how you can get a reference to GeneratorFunction and other related objects, borrowed from a V8 unit test file:

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

To tell if something is a Generator:

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

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

In the original question this answers isGenerator(gen) true. While g is a generator function. When you call the generator function you produce a generator.

But in most cases it's more important to ask if the thing is an iterator:

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

Usage:

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

This is iterating over a sequence. If there is an iterator embedded in that sequence that then use yield delegation to iterate through that before continuing the sequence. Generators aren't the only thing that produce useful iterators that you can yield to.

发布评论

评论列表(0)

  1. 暂无评论