여기에 대한 많은 대답은 정규 표현식을 사용합니다. 이것은 훌륭하지만 화살표 기능 및 클래스와 같이 언어에 대한 새로운 추가 기능을 너무 잘 처리하지 못합니다. 또한 축소 된 코드에서 이러한 함수를 사용하면 🔥으로 이동합니다. 축소 된 이름이 무엇이든 사용합니다. Angular는 DI 컨테이너에 등록 할 때 인수의 순서와 일치하는 정렬 된 문자열 배열을 전달할 수 있도록하여이 문제를 해결합니다. 솔루션과 마찬가지로 :
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');
};
원래 구문 분석 문제와 몇 가지 추가 기능 유형 (예 : 화살표 기능)을 처리합니다. 다음과 같이 처리 할 수있는 것과 처리 할 수없는 것에 대한 아이디어가 있습니다.
// 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);
});
});
ES6 프록시에 사용하려는 것에 따라 파괴하는 것이 가장 좋습니다. 예를 들어, 매개 변수 이름을 사용하여 종속성 주입에 사용하려는 경우 다음과 같이 할 수 있습니다.
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);
}
}
}
그것은 가장 진보 된 리졸버는 아니지만 간단한 DI에 args 파서를 사용하려는 경우 프록시를 사용하여 프록시를 처리하는 방법에 대한 아이디어를 제공합니다. 그러나이 방법에는 약간의 경고가 있습니다. 일반적인 매개 변수 대신 파괴적인 할당을 사용해야합니다. 인젝터 프록시를 전달할 때, 파괴는 객체에서 getter를 호출하는 것과 같습니다.
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));
이것은 다음을 출력합니다 :
{
"tweeter": {
"api": {
"client": {}
}
},
"timeline": {
"api": {
"client": {}
}
}
}
전체 응용 프로그램을 연결했습니다. 가장 좋은 점은 응용 프로그램을 테스트하기 쉽다는 것입니다 (각 클래스를 인스턴스화하고 mocks / stubs 등을 전달할 수 있습니다). 또한 구현을 교체해야하는 경우 한 곳에서 구현할 수 있습니다. JS 프록시 객체로 인해이 모든 것이 가능합니다.
참고 : 프로덕션 용도로 사용하기 전에이 작업을 수행해야하는 작업이 많이 있지만 모양에 대한 아이디어를 제공합니다.
답이 늦었지만 같은 것을 생각하는 다른 사람들에게 도움이 될 수 있습니다. 👍