Saya bermain dengan generator di Nodejs v0.11.2 dan I'm wondering bagaimana saya dapat memeriksa bahwa argumen ke fungsi generator fungsi.
Saya menemukan cara ini typeof f === 'fungsi' && Objek.getPrototypeOf(f) !== Objek.getPrototypeOf(Fungsi)
tapi aku'm tidak yakin jika ini adalah baik (dan bekerja di masa depan) cara.
Apa pendapat anda tentang masalah ini?
Dalam versi terbaru dari nodejs (saya terverifikasi dengan v0.11.12) anda dapat memeriksa apakah nama konstruktor sama dengan GeneratorFunction
. Saya don't tahu apa versi ini keluar, tapi itu bekerja.
function isGenerator(fn) {
return fn.constructor.name === 'GeneratorFunction';
}
Kami berbicara tentang hal ini di TC39 face-to-face meeting dan itu disengaja yang kita don't mengekspos cara untuk mendeteksi apakah fungsi generator atau tidak. Alasannya adalah bahwa setiap fungsi dapat mengembalikan iterable objek sehingga tidak masalah jika itu adalah fungsi atau generator fungsi.
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++;
}
}
Dua berperilaku identik (minus .membuang() tapi yang dapat ditambahkan juga)
ini bekerja di node dan di firefox:
var GeneratorFunction = (function*(){yield undefined;}).constructor;
function* test() {
yield 1;
yield 2;
}
console.log(test instanceof GeneratorFunction); // true
[jsfiddle][1]
Tapi itu tidak bekerja jika anda mengikat generator, misalnya:
foo = test.bind(bar);
console.log(foo instanceof GeneratorFunction); // false
I'm menggunakan ini:
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;
TJ Holowaychuk's co
perpustakaan memiliki fungsi untuk memeriksa apakah sesuatu adalah generator fungsi. Berikut adalah source code:
function isGeneratorFunction(obj) {
var constructor = obj.constructor;
if (!constructor) return false;
if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true;
return isGenerator(constructor.prototype);
}
Referensi: https://github.com/tj/co/blob/717b043371ba057cb7a4a2a4e47120d598116ed7/index.js#L221
Di node 7 anda dapat instanceof
terhadap konstruktor untuk mendeteksi kedua generator fungsi dan async fungsi:
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
Ini bekerja untuk semua keadaan dalam tes saya. Komentar di atas mengatakan itu doesn't bekerja untuk bernama generator fungsi ekspresi tapi aku'm dapat mereproduksi:
const genExprName=function*name(){};
genExprName instanceof GeneratorFunction; // true
(function*name2(){}) instanceof GeneratorFunction; // true
Satu-satunya masalah adalah .konstruktor
milik dari contoh dapat diubah. Jika seseorang benar-benar bertekad untuk menyebabkan anda masalah mereka bisa memecahkannya:
// 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
Seperti @Erik Arvidsson menyatakan, tidak ada standar-cara untuk memeriksa jika sebuah fungsi generator fungsi. Tapi anda bisa, pasti, hanya memeriksa untuk antarmuka, generator fungsi memenuhi:
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
}
Thats's ini.
Mozilla javascript dokumentasi yang menggambarkan Fungsi.prototipe.isGenerator
metode MDN API. Nodejs tampaknya tidak menerapkannya. Namun jika anda bersedia untuk membatasi kode untuk mendefinisikan generator dengan fungsi*
saja (tidak kembali iterable benda) anda bisa menambahkannya dengan menambahkannya sendiri dengan maju cek kompatibilitas:
if (typeof Function.prototype.isGenerator == 'undefined') {
Function.prototype.isGenerator = function() {
return /^function\s*\*/.test(this.toString());
}
}
Aku memeriksa bagaimana koa apakah itu dan mereka menggunakan perpustakaan ini: https://github.com/ljharb/is-generator-function.
Anda dapat menggunakannya seperti ini
const isGeneratorFunction = require('is-generator-function');
if(isGeneratorFunction(f)) {
...
}
The old school Objek.prototipe.toString.panggilan(val)
tampaknya untuk bekerja juga. Di Node versi 11.12.0 kembali [object Generator]
tapi paling terbaru Chrome dan Firefox kembali [objek GeneratorFunction]
.
Jadi bisa seperti ini:
function isGenerator(val) {
return /\[object Generator|GeneratorFunction\]/.test(Object.prototype.toString.call(val));
}
Kesulitan yang tidak dapat ditangani di sini, namun adalah bahwa jika anda menggunakan mengikat
metode pada generator fungsi, perubahan nama prototipe dari 'GeneratorFunction' untuk 'Fungsi'.
Ada's tidak netral Mencerminkan.mengikat
metode, tetapi anda bisa mendapatkan sekitar ini dengan ulang prototipe terikat operasi dengan operasi asli.
Misalnya:
const boundOperation = operation.bind(someContext, ...args)
console.log(boundOperation.constructor.name) // Function
Reflect.setPrototypeOf(boundOperation, operation)
console.log(boundOperation.constructor.name) // GeneratorFunction