javascript-检查函数是否为gen

我在Nodejs v0.11.2中使用了生成器,我想知道如何检查函数的自变量是生成器函数。

我找到了typeof f === 'function' && Object.getPrototypeOf(f) !== Object.getPrototypeOf(Function)的这种方式,但是我不确定这是否很好(并且将来会继续工作)。

您对此问题有何看法?

11个解决方案
43 votes

在最新版本的nodejs(我已通过v0.11.12验证)中,您可以检查构造函数名称是否等于GeneratorFunction

function isGenerator(fn) {
    return fn.constructor.name === 'GeneratorFunction';
}
smitt04 answered 2020-01-05T22:18:43Z
38 votes

我们在TC39面对面的会议中谈到了这一点,这是故意的,我们没有公开检测函数是否为生成器的方法。 原因是任何函数都可以返回可迭代对象,因此无论是函数还是生成器函数都无关紧要。

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

这两个行为相同(减.throw(),但也可以添加)

Erik Arvidsson answered 2020-01-05T22:18:23Z
9 votes

我正在使用这个:

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;
Albert answered 2020-01-05T22:19:03Z
9 votes

这适用于节点和Firefox:

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

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

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

jsfiddle

但是,如果您绑定了生成器,那么它将不起作用,例如:

foo = test.bind(bar); 
console.log(foo instanceof GeneratorFunction); // false
Nick Sotiros answered 2020-01-05T22:19:32Z
6 votes

TJ Holowaychuk co库具有检查某些内容是否为生成器功能的最佳功能。 这是源代码:

function isGeneratorFunction(obj) {
   var constructor = obj.constructor;
   if (!constructor) return false;
   if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true;
   return isGenerator(constructor.prototype);
}

参考:[https://github.com/tj/co/blob/717b043371ba057cb7a4a2a4e47120d598116ed7/index.js#L221]

freddyrangel answered 2020-01-05T22:19:56Z
4 votes

在节点7中,您可以对构造函数进行.constructor的检测,以同时检测生成器函数和异步函数:

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

这适用于我的测试中的所有情况。 上面的评论说它不适用于命名生成器函数表达式,但是我无法复制:

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

唯一的问题是可以更改实例的.constructor属性。 如果某人真的下定决心要给您造成问题,则可以解决该问题:

// 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
Lycan answered 2020-01-05T22:20:26Z
2 votes

Mozilla javascript文档描述了Function.prototype.isGenerator方法MDN API。 Nodejs似乎没有实现它。 但是,如果您希望将代码限制为仅使用function*定义生成器(不返回可迭代的对象),则可以通过自己添加前向兼容性检查来对其进行扩充:

if (typeof Function.prototype.isGenerator == 'undefined') {
    Function.prototype.isGenerator = function() {
        return /^function\s*\*/.test(this.toString());
    }
}
Lex answered 2020-01-05T22:20:46Z
2 votes

如@Erik Arvidsson所述,没有标准方法可以检查函数是否为生成器函数。 但是可以肯定的是,只需检查接口,生成器函数即可满足:

function* fibonacci(prevPrev, prev) {

  while (true) {

    let next = prevPrev + prev;

    yield next;

    prevPrev = prev;
    prev = next;
  }
}

// fetch get an instance
let fibonacciGenerator = fibonacci(2, 3)

// check the interface
if (typeof fibonacciGenerator[Symbol.iterator] == 'function' && 
    typeof fibonacciGenerator['next'] == 'function' &&
    typeof fibonacciGenerator['throw'] == 'function') {

  // it's safe to assume the function is a generator function or a shim that behaves like a generator function

  let nextValue = fibonacciGenerator.next().value; // 5
}

而已。

kyr0 answered 2020-01-05T22:21:10Z
1 votes

我检查了koa的工作方式,然后他们使用了这个库:[https://github.com/ljharb/is-generator-function。]

你可以这样使用

const isGeneratorFunction = require('is-generator-function');
if(isGeneratorFunction(f)) {
    ...
}
kraf answered 2020-01-05T22:21:35Z
0 votes

此处尚未解决的一个难题是,如果在生成器函数上使用Reflect.bind方法,则它将其原型名称从“ GeneratorFunction”更改为“ Function”。

没有中立的Reflect.bind方法,但是您可以通过将绑定操作的原型重置为原始操作的原型来解决此问题。

例如:

const boundOperation = operation.bind(someContext, ...args)
console.log(boundOperation.constructor.name)       // Function
Reflect.setPrototypeOf(boundOperation, operation)
console.log(boundOperation.constructor.name)       // GeneratorFunction
Lorenzo answered 2020-01-05T22:22:04Z
0 votes

老派Object.prototype.toString.call(val)似乎也能正常工作。 在Node版本11.12.0中,它返回[object Generator],但最新的Chrome和Firefox返回[object GeneratorFunction]

因此可能是这样的:

function isGenerator(val) {
    return /\[object Generator|GeneratorFunction\]/.test(Object.prototype.toString.call(val));

}
Mario Škrlec answered 2020-01-05T22:22:28Z
translate from https://stackoverflow.com:/questions/16754956/check-if-function-is-a-generator