Javascript 检查功能是否为发电机

Javascript 检查功能是否为发电机,javascript,generator,yield,ecmascript-6,Javascript,Generator,Yield,Ecmascript 6,我在Nodejs v0.11.2中玩过发电机,我想知道 如何检查函数的参数是生成器函数 var iterator = Symbol.iterator; function notAGenerator() { var count = 0; return { [iterator]: function() { return this; }, next: function() { return {value: count++, done: fal

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

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++;
  }
}
我是这样发现的
typeof f=='function'&&Object.getPrototypeOf(f)!=getPrototypeOf(函数)
,但我不确定这是否是一种好的方法(将来也可以使用)


您对这个问题的看法是什么?

我们在TC39面对面会议上讨论了这一点,我们有意不公开检测函数是否为生成器的方法。原因是任何函数都可以返回一个iterable对象,所以不管它是函数还是生成器函数

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

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

if (typeof Function.prototype.isGenerator == 'undefined') {
    Function.prototype.isGenerator = function() {
        return /^function\s*\*/.test(this.toString());
    }
}
我用这个:

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;

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

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

这适用于node和firefox:

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

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

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

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

foo = test.bind(bar); 
console.log(foo instanceof GeneratorFunction); // false
const boundOperation = operation.bind(someContext, ...args)
console.log(boundOperation.constructor.name)       // Function
Reflect.setPrototypeOf(boundOperation, operation)
console.log(boundOperation.constructor.name)       // GeneratorFunction

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

参考:

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

这里还没有提到的一个困难是,如果对生成器函数使用
bind
方法,它会将其原型的名称从“GeneratorFunction”更改为“function”

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

例如:

foo = test.bind(bar); 
console.log(foo instanceof GeneratorFunction); // false
const boundOperation = operation.bind(someContext, ...args)
console.log(boundOperation.constructor.name)       // Function
Reflect.setPrototypeOf(boundOperation, operation)
console.log(boundOperation.constructor.name)       // GeneratorFunction
我检查了它是如何使用的,他们使用了这个库:

你可以这样使用它

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

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

就是这样。

老式的
Object.prototype.toString.call(val)
似乎也能用。在节点版本11.12.0中,它返回
[object Generator]
,但最新的Chrome和Firefox返回
[object GeneratorFunction]

所以可能是这样的:

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

}
函数isGenerator(目标){
返回目标[Symbol.toStringTag]=“GeneratorFunction”;
}

函数isGenerator(目标){
返回Object.prototype.toString.call(目标)=='[Object GeneratorFunction]';
}

非常确定
f GeneratorFunction的instanceof
应该可以工作,基于15.19.3.1,当前ES6 draft.nodejs v0.11.2的GeneratorFunction构造函数没有GeneratorFunction,所以我认为v8 v3.19.0也没有。但是,是的,这个检查会简单得多。这个构造函数似乎是对最新草案的新添加。搜索上一个,我找不到该文本。假设它保持在规范中,我会想象它会在某个时候出现。编辑:…啊,是的,我在更改注释“为生成器函数和生成器方法定义添加了语义”中看到了它…看起来它是在大约10天前登陆的。我看到这个更改是从v8中删除的,因为一些测试问题,必须有一些东西!!!生成器不同于函数…哇。。。太糟糕了:(无法确定是生成器函数还是简单函数不允许好的东西,比如与primise库集成(如Q.async)来自动检测生成器,并在那里获取/推送值以获得好的、干净的“primise”基于生成器的api。@Erik Arvidsson我们在哪里可以找到符号函数的文档?我必须注意,即使是最新版本的Node.js,这个代码段也不起作用,我在
[iterator]:function()中得到了一个
意外的标记[
){
。这是从哪里来的?@LexPodgorny我发现很少提到符号,@Erik,所以你是说一个生成函数只是一个特殊的函数类,没有什么不同?那么也许我们可以通过检查一个函数是否具有生成函数的所有特征来判断它是否是一个生成函数(返回包含<代码>下的< <代码> > [迭代器] <代码> >代码>下<代码>返回>代码>值和<代码>计数>代码>等)这是否在可预见的将来始终工作?您可能想考虑可能存在的空白。<代码>函数(ARGS){} <代码>或<代码>函数*(ARGS){}我都看过。如果node本机添加了一个检测器,我不会感到惊讶,因为toString太贵了。我将其缩短为
生成器=(函数*(){})。构造函数;生成器的g实例
,不幸的是
(函数*(){}).prototype.constructor
不是用于检查生成器迭代器的instanceof的有效参数“您可以使用obj.constructor.name检查对象的“类”,尽管有一些警告,但这只适用于函数声明和匿名函数,而不适用于命名函数表达式。非常好的解决方案,谢谢!为今天的JS更新了:
const isGenerator=fn=>['GeneratorFunction','AsyncGeneratorFunction']。包括(fn.constructor.name)
。异步生成器a