检查功能是否是发电机

Dim*_*ich 54 javascript yield generator ecmascript-6

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

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

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

smi*_*t04 43

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

function isGenerator(fn) {
    return fn.constructor.name === 'GeneratorFunction';
}
Run Code Online (Sandbox Code Playgroud)

  • 好的解决方案,谢谢!更新了今天的JS:`const isGenerator = fn => ['GeneratorFunction','AsyncGeneratorFunction'].includes(fn.constructor.name)`.异步生成器是ES2018的一部分,可在节点v10中找到,请参见[node.green](https://node.green/#ES2018-features-Asynchronous-Iterators-async-generators) (3认同)
  • 这仅适用于函数声明和匿名函数,它不适用于命名函数表达式. (2认同)

Eri*_*son 39

我们在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++;
  }
}
Run Code Online (Sandbox Code Playgroud)

这两个表现相同(减去.throw(),但也可以添加)

  • 哇......太糟糕了:(不能确定它是生成器函数还是简单函数不会允许很好的东西,比如与preise库(如Q.async)的集成来自动检测生成器并获取/推送值以获得良好的效果基于发电机清洁"primise"api. (4认同)
  • @Erik Arvidsson我们在哪里可以找到Symbol功能的文档? (2认同)
  • 我必须注意,即使使用最新的开发版本的Node.js,这个片段也不起作用,我在`[iterator]:function(){`得到一个`Unexpected token [`.它来自哪里? (2认同)

Nic*_*ros 10

这适用于节点和firefox:

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

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

console.log(test instanceof GeneratorFunction); // true
Run Code Online (Sandbox Code Playgroud)

的jsfiddle

但是如果绑定生成器它不起作用,例如:

foo = test.bind(bar); 
console.log(foo instanceof GeneratorFunction); // false
Run Code Online (Sandbox Code Playgroud)


Alb*_*ert 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;
Run Code Online (Sandbox Code Playgroud)

  • 我把它缩短为`Generator =(function*(){}).constructor; g instanceof Generator`,不幸的是`(function*(){}).prototype.constructor`不是instanceof的有效参数,用于检查生成器迭代器 (2认同)

fre*_*gel 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);
}
Run Code Online (Sandbox Code Playgroud)

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


小智 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
Run Code Online (Sandbox Code Playgroud)

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

const genExprName=function*name(){};
genExprName instanceof GeneratorFunction;            // true
(function*name2(){}) instanceof GeneratorFunction;   // true
Run Code Online (Sandbox Code Playgroud)

唯一的问题是.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
Run Code Online (Sandbox Code Playgroud)


kyr*_*yr0 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
}
Run Code Online (Sandbox Code Playgroud)

就是这样。

  • 如果Symbol.iterator像鸭子,next像鸭子,并且 throw像鸭子,那么...... (2认同)