Banyak jawaban di sini menggunakan regex, ini bagus tapi tidak terlalu baik untuk penambahan bahasa (seperti fungsi panah dan kelas). Yang juga perlu diperhatikan adalah bahwa jika Anda menggunakan salah satu dari fungsi ini pada kode yang diperkecil maka ia akan pergi 🔥. Itu akan menggunakan apa pun nama yang diperkecil itu. Sudut menyiasatinya dengan memungkinkan Anda untuk mengirimkan serangkaian string yang cocok dengan urutan argumen saat mendaftarkannya dengan wadah DI. Demikian seterusnya dengan solusinya:
var esprima = require('esprima');
var _ = require('lodash');
const parseFunctionArguments = (func) => {
// allows us to access properties that may or may not exist without throwing
// TypeError: Cannot set property 'x' of undefined
const maybe = (x) => (x || {});
// handle conversion to string and then to JSON AST
const functionAsString = func.toString();
const tree = esprima.parse(functionAsString);
console.log(JSON.stringify(tree, null, 4))
// We need to figure out where the main params are. Stupid arrow functions 👊
const isArrowExpression = (maybe(_.first(tree.body)).type == 'ExpressionStatement');
const params = isArrowExpression ? maybe(maybe(_.first(tree.body)).expression).params
: maybe(_.first(tree.body)).params;
// extract out the param names from the JSON AST
return _.map(params, 'name');
};
Ini menangani masalah parse asli dan beberapa tipe fungsi lainnya (mis. Fungsi panah). Inilah gagasan tentang apa yang bisa dan tidak bisa ditangani sebagaimana adanya:
// I usually use mocha as the test runner and chai as the assertion library
describe('Extracts argument names from function signature. 💪', () => {
const test = (func) => {
const expectation = ['it', 'parses', 'me'];
const result = parseFunctionArguments(toBeParsed);
result.should.equal(expectation);
}
it('Parses a function declaration.', () => {
function toBeParsed(it, parses, me){};
test(toBeParsed);
});
it('Parses a functional expression.', () => {
const toBeParsed = function(it, parses, me){};
test(toBeParsed);
});
it('Parses an arrow function', () => {
const toBeParsed = (it, parses, me) => {};
test(toBeParsed);
});
// ================= cases not currently handled ========================
// It blows up on this type of messing. TBH if you do this it deserves to
// fail 😋 On a tech note the params are pulled down in the function similar
// to how destructuring is handled by the ast.
it('Parses complex default params', () => {
function toBeParsed(it=4*(5/3), parses, me) {}
test(toBeParsed);
});
// This passes back ['_ref'] as the params of the function. The _ref is a
// pointer to an VariableDeclarator where the ✨🦄 happens.
it('Parses object destructuring param definitions.' () => {
function toBeParsed ({it, parses, me}){}
test(toBeParsed);
});
it('Parses object destructuring param definitions.' () => {
function toBeParsed ([it, parses, me]){}
test(toBeParsed);
});
// Classes while similar from an end result point of view to function
// declarations are handled completely differently in the JS AST.
it('Parses a class constructor when passed through', () => {
class ToBeParsed {
constructor(it, parses, me) {}
}
test(ToBeParsed);
});
});
Bergantung pada apa yang ingin Anda gunakan untuk ES6 Proxy dan perusakan mungkin merupakan pilihan terbaik Anda. Misalnya jika Anda ingin menggunakannya untuk injeksi dependensi (menggunakan nama params) maka Anda dapat melakukannya sebagai berikut:
class GuiceJs {
constructor() {
this.modules = {}
}
resolve(name) {
return this.getInjector()(this.modules[name]);
}
addModule(name, module) {
this.modules[name] = module;
}
getInjector() {
var container = this;
return (klass) => {
console.log(klass);
var paramParser = new Proxy({}, {
// The `get` handler is invoked whenever a get-call for
// `injector.*` is made. We make a call to an external service
// to actually hand back in the configured service. The proxy
// allows us to bypass parsing the function params using
// taditional regex or even the newer parser.
get: (target, name) => container.resolve(name),
// You shouldn't be able to set values on the injector.
set: (target, name, value) => {
throw new Error(`Don't try to set ${name}! 😑`);
}
})
return new klass(paramParser);
}
}
}
Ini bukan resolver paling canggih di luar sana tetapi memberikan gambaran tentang bagaimana Anda dapat menggunakan Proxy untuk menanganinya jika Anda ingin menggunakan args parser untuk DI sederhana. Namun ada satu peringatan kecil dalam pendekatan ini. Kita perlu menggunakan penugasan yang merusak alih-alih params yang normal. Ketika kami melewati proxy injector, destrukturisasi sama dengan memanggil pengambil pada objek.
class App {
constructor({tweeter, timeline}) {
this.tweeter = tweeter;
this.timeline = timeline;
}
}
class HttpClient {}
class TwitterApi {
constructor({client}) {
this.client = client;
}
}
class Timeline {
constructor({api}) {
this.api = api;
}
}
class Tweeter {
constructor({api}) {
this.api = api;
}
}
// Ok so now for the business end of the injector!
const di = new GuiceJs();
di.addModule('client', HttpClient);
di.addModule('api', TwitterApi);
di.addModule('tweeter', Tweeter);
di.addModule('timeline', Timeline);
di.addModule('app', App);
var app = di.resolve('app');
console.log(JSON.stringify(app, null, 4));
Ini menghasilkan sebagai berikut:
{
"tweeter": {
"api": {
"client": {}
}
},
"timeline": {
"api": {
"client": {}
}
}
}
Ini menghubungkan seluruh aplikasi. Bit terbaik adalah bahwa aplikasi ini mudah untuk diuji (Anda bisa langsung membuat instance setiap kelas dan lulus mengejek / bertopik / dll). Juga jika Anda perlu menukar implementasi, Anda dapat melakukannya dari satu tempat. Semua ini dimungkinkan karena objek JS Proxy.
Catatan: Ada banyak pekerjaan yang perlu dilakukan untuk ini sebelum akan siap untuk penggunaan produksi tetapi itu memberikan gambaran seperti apa bentuknya.
Ini agak terlambat dalam jawabannya tetapi mungkin membantu orang lain yang memikirkan hal yang sama. 👍