it-swarm.com.de

prüfen Sie, ob die Funktion ein Generator ist

Ich habe mit Generatoren in Nodejs v0.11.2 gespielt, und ich frage mich , Wie ich dieses Argument meiner Funktion überprüfen kann: Generatorfunktion.

Ich fand diesen Weg typeof f === 'function' && Object.getPrototypeOf(f) !== Object.getPrototypeOf(Function), aber ich bin nicht sicher, ob dies gut ist (und in Zukunft funktionieren wird).

Wie ist Ihre Meinung zu diesem Thema?

45
Dima Vidmich

Wir haben darüber in den TC39-Meetings von Angesicht zu Angesicht gesprochen, und es ist bewusst, dass wir keine Möglichkeit offenlegen, festzustellen, ob eine Funktion ein Generator ist oder nicht. Der Grund ist, dass jede Funktion ein iterierbares Objekt zurückgeben kann. Es spielt also keine Rolle, ob es sich um eine Funktion oder eine Generatorfunktion handelt.

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

Diese beiden verhalten sich identisch (minus. Throw (), aber das kann auch hinzugefügt werden)

32
Erik Arvidsson

In der neuesten Version von nodejs (ich habe es mit v0.11.12 verifiziert) können Sie überprüfen, ob der Konstruktorname GeneratorFunction ist. Ich weiß nicht, in welcher Version das veröffentlicht wurde, aber es funktioniert.

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

Ich benutze das:

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;
9
Albert

das funktioniert im Knoten und im Firefox:

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

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

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

jsfiddle

Es funktioniert jedoch nicht, wenn Sie einen Generator binden:

foo = test.bind(bar); 
console.log(foo instanceof GeneratorFunction); // false
7
Nick Sotiros

Die co-Bibliothek von TJ Holowaychuk hat die beste Funktion, um zu überprüfen, ob etwas eine Generatorfunktion ist. Hier ist der Quellcode:

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

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

6
freddyrangel

In Knoten 7 können Sie instanceof gegen die Konstruktoren abfragen, um sowohl Generatorfunktionen als auch Asynchronfunktionen zu erkennen:

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

Dies funktioniert unter allen Umständen in meinen Tests. Ein Kommentar oben besagt, dass es nicht für benannte Generator-Funktionsausdrücke funktioniert, aber ich kann es nicht reproduzieren:

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

Das einzige Problem ist, dass die .constructor-Eigenschaft von Instanzen geändert werden kann. Wenn jemand wirklich entschlossen war, Ihnen Probleme zu bereiten, könnte er es brechen:

// 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
4
Lycan

Wie @Erik Arvidsson feststellte, gibt es keine Standardmethode, um zu überprüfen, ob eine Funktion eine Generatorfunktion ist. Sie können aber sicher nur nach der Schnittstelle suchen, die eine Generatorfunktion erfüllt:

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
}

Das ist es.

2
kyr0

Die Javascript-Dokumentation von Mozilla beschreibt Function.prototype.isGenerator method MDN API . Nodejs scheint es nicht zu implementieren. Wenn Sie jedoch bereit sind, Ihren Code darauf zu beschränken, Generatoren nur mit function* zu definieren (keine zurückgegebenen Objekte), können Sie ihn erweitern, indem Sie ihn selbst mit einer Vorwärtskompatibilitätsprüfung hinzufügen:

if (typeof Function.prototype.isGenerator == 'undefined') {
    Function.prototype.isGenerator = function() {
        return /^function\s*\*/.test(this.toString());
    }
}
2
Lex

Ich habe überprüft, wie koa es tut, und sie verwenden diese Bibliothek: https://github.com/ljharb/isgenerator-function .

Sie können es so verwenden

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

Eine Schwierigkeit, die hier noch nicht angesprochen wurde, besteht darin, dass wenn Sie die bind-Methode für die Generatorfunktion verwenden, der Name seines Prototyps von 'GeneratorFunction' in 'Function' geändert wird.

Es gibt keine neutrale Reflect.bind-Methode, aber Sie können dies umgehen, indem Sie den Prototyp der gebundenen Operation auf den der ursprünglichen Operation zurücksetzen.

Zum Beispiel:

const boundOperation = operation.bind(someContext, ...args)
console.log(boundOperation.constructor.name)       // Function
Reflect.setPrototypeOf(boundOperation, operation)
console.log(boundOperation.constructor.name)       // GeneratorFunction
0
Lorenzo

Die alte Schule Object.prototype.toString.call(val) scheint auch zu funktionieren. In Node Version 11.12.0 gibt es [object Generator] Zurück, aber die neuesten Chrome und Firefox geben [object GeneratorFunction] Zurück.

Könnte also so sein:

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

}
0
Mario Škrlec