javascript - soljson - solidity español
verificar si la función es un generador (10)
Jugué con generadores en Nodejs v0.11.2 y me pregunto cómo puedo verificar ese argumento para que mi función sea la función de generador.
Encontré de esta forma typeof f === ''function'' && Object.getPrototypeOf(f) !== Object.getPrototypeOf(Function)
pero no estoy seguro de si esto es bueno (y funciona en el futuro).
¿Cuál es tu opinión sobre este tema?
La documentación de Mozilla javascript describe el método Function.prototype.isGenerator
MDN API . Nodejs no parece implementarlo. Sin embargo, si está dispuesto a limitar su código para definir generadores con function*
solamente (sin devolver objetos iterables), puede aumentarlo agregándolo usted mismo con una verificación de compatibilidad futura:
if (typeof Function.prototype.isGenerator == ''undefined'') {
Function.prototype.isGenerator = function() {
return /^function/s*/*/.test(this.toString());
}
}
Estoy usando esto:
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;
En la última versión de nodejs (verifiqué con v0.11.12) puedes verificar si el nombre del constructor es igual a GeneratorFunction
. No sé en qué versión salió esto, pero funciona.
function isGenerator(fn) {
return fn.constructor.name === ''GeneratorFunction'';
}
La biblioteca de co
TJ Holowaychuk tiene la mejor función para verificar si algo es una función del generador. Aquí está el código fuente:
function isGeneratorFunction(obj) {
var constructor = obj.constructor;
if (!constructor) return false;
if (''GeneratorFunction'' === constructor.name || ''GeneratorFunction'' === constructor.displayName) return true;
return isGenerator(constructor.prototype);
}
Referencia: https://github.com/tj/co/blob/717b043371ba057cb7a4a2a4e47120d598116ed7/index.js#L221
Hablamos de esto en las reuniones cara a cara de TC39 y es deliberado que no exponemos una forma de detectar si una función es un generador o no. La razón es que cualquier función puede devolver un objeto iterable, por lo que no importa si se trata de una función o una función del generador.
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++;
}
}
Estos dos se comportan de manera idéntica (menos .throw () pero eso se puede agregar también)
En el nodo 7 puedes instanceof
contra los constructores para detectar tanto funciones del generador como funciones asíncronas:
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
Esto funciona para todas las circunstancias en mis pruebas. Un comentario anterior dice que no funciona para expresiones de funciones de generador con nombre, pero no puedo reproducir:
const genExprName=function*name(){};
genExprName instanceof GeneratorFunction; // true
(function*name2(){}) instanceof GeneratorFunction; // true
El único problema es que la propiedad .constructor
de instancias se puede cambiar. Si alguien estaba realmente decidido a causarte problemas, podrían romperlo:
// 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
Una dificultad no abordada aquí todavía es que si usa el método bind
en la función del generador, cambia el nombre de su prototipo de ''GeneratorFunction'' a ''Function''.
No hay un método neutral Reflect.bind
, pero puede evitar esto restableciendo el prototipo de la operación vinculada a la de la operación original.
Por ejemplo:
const boundOperation = operation.bind(someContext, ...args)
console.log(boundOperation.constructor.name) // Function
Reflect.setPrototypeOf(boundOperation, operation)
console.log(boundOperation.constructor.name) // GeneratorFunction
Como dijo @Erik Arvidsson, no hay una forma estándar de verificar si una función es una función de generador. Pero puedes estar seguro de que solo verificas la interfaz, una función del generador cumple:
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
}
Eso es todo.
Comprobé cómo lo hace koa y usan esta biblioteca: https://github.com/ljharb/is-generator-function .
Puedes usarlo así
const isGeneratorFunction = require(''is-generator-function'');
if(isGeneratorFunction(f)) {
...
}
esto funciona en node y en firefox:
var GeneratorFunction = (function*(){yield undefined;}).constructor;
function* test() {
yield 1;
yield 2;
}
console.log(test instanceof GeneratorFunction); // true
Pero no funciona si enlaza un generador, por ejemplo:
foo = test.bind(bar);
console.log(foo instanceof GeneratorFunction); // false