Как определить генератор ES6
Скажем, у меня есть функция генератора, как это:
var g = function*() {
yield 1;
yield 2;
yield 3;
};
var gen = g();
Как я могу программно сказать, что g
является функцией генератора, или что gen
такое итератор?
Это похоже на одну возможность:
g.constructor.name === 'GeneratorFunction'
Есть ли способ лучше?
Обновление: я закончил тем, что использовал подход, подобный ответу Эрика, но используя eval
сначала определить, поддерживаются ли генераторы на целевой платформе. Вот реализация:
var GeneratorConstructor = (function() {
try {
var generator;
return eval('generator = function*() { yield 1; };').constructor;
} catch (e) {
// If the above throws a SyntaxError, that means generators aren't
// supported on the current platform, which means isGenerator should
// always return false. So we'll return an anonymous function here, so
// that instanceof checks will always return false.
return function() {};
}
}());
/**
* Checks whether a function is an ES6 Harmony generator.
*
* @private
* @param {Function} fn
* @returns {boolean}
*/
function isGenerator(fn) {
return fn instanceof GeneratorConstructor;
}
3 ответа
Объединяя ваше решение с другими решениями, это избавляет от необходимости глобального GeneratorFunction
:
g instanceof (function*() {}).constructor
Следующее изображение из текущего проекта ES6 довольно информативно для демонстрации взаимосвязей между функциями генератора и другими объектами:
Похоже, вы должны быть в состоянии использовать g instanceof GeneratorFunction
если у вас есть глобальная ссылка на GeneratorFunction
В противном случае я думаю, что ваш нынешний подход просто отлично.
Вот как вы можете получить ссылку на GeneratorFunction
и другие связанные объекты, заимствованные из файла модульного тестирования V8:
function* g() { yield 1; }
var GeneratorFunctionPrototype = Object.getPrototypeOf(g);
var GeneratorFunction = GeneratorFunctionPrototype.constructor;
var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype;
Чтобы сказать, если что-то является генератором:
const g = (function*() {yield 1;});
const GeneratorFunctionPrototype = Object.getPrototypeOf(g);
function isGenerator(thing) {
return typeof thing === 'object' && thing.constructor === GeneratorFunctionPrototype;
}
В оригинальном вопросе это ответы isGenerator(gen)
правда. В то время как g
является функцией генератора. Когда вы вызываете функцию генератора, вы создаете генератор.
Но в большинстве случаев более важно спросить, является ли эта вещь итератором:
function isIterator(thing) {
// If thing has a Symbol.iterator
return typeof thing === 'object' && thing[Symbol.iterator];
}
Использование:
function *Pseq(list, repeats=1) {
for (let i = 0; i < repeats; i++) {
for (let value of list) {
if (isIterator(value)) {
yield *value;
} else {
yield value;
}
}
}
}
let q = Pseq([1, 2, 3, Pseq([10, 20, 30], 2), 4], 2);
for (let v of q) {
console.log(v);
}
1 2 3 10 20 30 10 20 30 4 1 2 3 4
Это повторение последовательности. Если в эту последовательность встроен итератор, который использует делегирование yield для итерации, перед тем как продолжить последовательность. Генераторы - не единственное, что создает полезные итераторы, которые вы можете использовать.