check if function is a generator

2019-01-07 15:50发布

I played with generators in Nodejs v0.11.2 and I'm wondering how I can check that argument to my function is generator function.

I found this way typeof f === 'function' && Object.getPrototypeOf(f) !== Object.getPrototypeOf(Function) but I'm not sure if this is good (and working in future) way.

What is your opinion about this issue?

10条回答
不美不萌又怎样
2楼-- · 2019-01-07 16:07

In node 7 you can instanceof against the constructors to detect both generator functions and async functions:

const GeneratorFunction = function*(){}.constructor;
const AsyncFunction = async function(){}.constructor;

function norm(){}
function*gen(){}
async function as(){}

norm instanceof Function;              // true
norm instanceof GeneratorFunction;     // false
norm instanceof AsyncFunction;         // false

gen instanceof Function;               // true
gen instanceof GeneratorFunction;      // true
gen instanceof AsyncFunction;          // false

as instanceof Function;                // true
as instanceof GeneratorFunction;       // false
as instanceof AsyncFunction;           // true

This works for all circumstances in my tests. A comment above says it doesn't work for named generator function expressions but I'm unable to reproduce:

const genExprName=function*name(){};
genExprName instanceof GeneratorFunction;            // true
(function*name2(){}) instanceof GeneratorFunction;   // true

The only problem is the .constructor property of instances can be changed. If someone was really determined to cause you problems they could break it:

// Bad people doing bad things
const genProto = function*(){}.constructor.prototype;
Object.defineProperty(genProto,'constructor',{value:Boolean});

// .. sometime later, we have no access to GeneratorFunction
const GeneratorFunction = function*(){}.constructor;
GeneratorFunction;                     // [Function: Boolean]
function*gen(){}
gen instanceof GeneratorFunction;      // false
查看更多
神经病院院长
3楼-- · 2019-01-07 16:07

I checked how koa does it and they use this library: https://github.com/ljharb/is-generator-function.

You can use it like this

const isGeneratorFunction = require('is-generator-function');
if(isGeneratorFunction(f)) {
    ...
}
查看更多
老娘就宠你
4楼-- · 2019-01-07 16:10

Mozilla javascript documentation describes Function.prototype.isGenerator method MDN API. Nodejs does not seem to implement it. However if you are willing to limit your code to defining generators with function* only (no returning iterable objects) you can augment it by adding it yourself with a forward compatibility check:

if (typeof Function.prototype.isGenerator == 'undefined') {
    Function.prototype.isGenerator = function() {
        return /^function\s*\*/.test(this.toString());
    }
}
查看更多
手持菜刀,她持情操
5楼-- · 2019-01-07 16:13

this works in node and in firefox:

var GeneratorFunction = (function*(){yield undefined;}).constructor;

function* test() {
   yield 1;
   yield 2;
}

console.log(test instanceof GeneratorFunction); // true

jsfiddle

But it does not work if you bind a generator, for example:

foo = test.bind(bar); 
console.log(foo instanceof GeneratorFunction); // false
查看更多
成全新的幸福
6楼-- · 2019-01-07 16:16

I'm using this:

var sampleGenerator = function*() {};

function isGenerator(arg) {
    return arg.constructor === sampleGenerator.constructor;
}
exports.isGenerator = isGenerator;

function isGeneratorIterator(arg) {
    return arg.constructor === sampleGenerator.prototype.constructor;
}
exports.isGeneratorIterator = isGeneratorIterator;
查看更多
冷血范
7楼-- · 2019-01-07 16:17

We talked about this in the TC39 face-to-face meetings and it is deliberate that we don't expose a way to detect whether a function is a generator or not. The reason is that any function can return an iterable object so it does not matter if it is a function or a generator function.

var iterator = Symbol.iterator;

function notAGenerator() {
  var  count = 0;
  return {
    [iterator]: function() {
      return this;
    },
    next: function() {
      return {value: count++, done: false};
    }
  }
}

function* aGenerator() {
  var count = 0;
  while (true) {
    yield count++;
  }
}

These two behave identical (minus .throw() but that can be added too)

查看更多
登录 后发表回答