64

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

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

你对这个问题有什么看法?

4

12 回答 12

52

在最新版本的 nodejs(我用 v0.11.12 验证)中,您可以检查构造函数名称是否等于GeneratorFunction. 我不知道这是什么版本,但它可以工作。

function isGenerator(fn) {
    return fn.constructor.name === 'GeneratorFunction';
}
于 2014-03-24T13:11:34.880 回答
52

我们在 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() 但也可以添加)

于 2013-10-29T14:00:28.493 回答
13

这适用于节点和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
于 2014-07-23T03:32:08.333 回答
9

我正在使用这个:

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;
于 2014-03-21T08:44:25.727 回答
6

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

于 2016-06-16T16:55:31.983 回答
6

在节点 7 中,您可以instanceof针对构造函数来检测生成器函数和异步函数:

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
于 2016-11-20T13:01:18.153 回答
5

正如@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
}

就是这样。

于 2017-06-07T12:53:50.073 回答
2

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

if (typeof Function.prototype.isGenerator == 'undefined') {
    Function.prototype.isGenerator = function() {
        return /^function\s*\*/.test(this.toString());
    }
}
于 2014-01-07T16:41:51.630 回答
2

老学校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));

}
于 2019-05-18T10:53:14.957 回答
2
function isGenerator(target) {
  return target[Symbol.toStringTag] === 'GeneratorFunction';
}

或者

function isGenerator(target) {
  return Object.prototype.toString.call(target) === '[object GeneratorFunction]';
}
于 2020-06-24T08:19:31.020 回答
2

我检查了koa是如何做到的,他们使用了这个库:https ://github.com/ljharb/is-generator-function 。

你可以像这样使用它

const isGeneratorFunction = require('is-generator-function');
if(isGeneratorFunction(f)) {
    ...
}
于 2017-06-07T11:33:04.250 回答
0

这里尚未解决的一个困难是,如果您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
于 2017-04-09T16:51:55.513 回答