检查功能是否是发电机

时间:2013-05-25 23:50:14

标签: javascript generator yield ecmascript-6

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

我发现这种方式typeof f === 'function' && Object.getPrototypeOf(f) !== Object.getPrototypeOf(Function),但我不确定这是否合适(以及将来合作)。

您对此问题有何看法?

11 个答案:

答案 0 :(得分:41)

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

function isGenerator(fn) {
    return fn.constructor.name === 'GeneratorFunction';
}

答案 1 :(得分:37)

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

答案 2 :(得分: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;

答案 3 :(得分:8)

这适用于节点和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

答案 4 :(得分: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

答案 5 :(得分:4)

在节点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

答案 6 :(得分:2)

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

答案 7 :(得分:2)

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

多数民众赞成的。

答案 8 :(得分:1)

我查看koa如何做,并使用此库:https://github.com/ljharb/is-generator-function

您可以像这样使用

const isGeneratorFunction = require('is-generator-function');
if(isGeneratorFunction(f)) {
    ...
}

答案 9 :(得分:1)

function isGenerator(target) {
  return target[Symbol.toStringTag] === 'GeneratorFunction';
}

function isGenerator(target) {
  return Object.prototype.toString.call(target) === '[object GeneratorFunction]';
}

答案 10 :(得分: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