Rất nhiều câu trả lời ở đây sử dụng regexes, điều này tốt nhưng nó không xử lý các bổ sung mới cho ngôn ngữ quá tốt (như các hàm và lớp mũi tên). Cũng cần lưu ý là nếu bạn sử dụng bất kỳ chức năng nào trong số các mã được rút gọn này thì nó sẽ hoạt động. Nó sẽ sử dụng bất cứ tên rút gọn nào. Angular khắc phục điều này bằng cách cho phép bạn chuyển vào một chuỗi các chuỗi được sắp xếp khớp với thứ tự của các đối số khi đăng ký chúng với vùng chứa DI. Vì vậy, với giải pháp:
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ều này xử lý vấn đề phân tích cú pháp gốc và một vài loại hàm khác (ví dụ: hàm mũi tên). Đây là một ý tưởng về những gì nó có thể và không thể xử lý như sau:
// 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);
});
});
Tùy thuộc vào những gì bạn muốn sử dụng nó cho ES6 Proxy và phá hủy có thể là lựa chọn tốt nhất của bạn. Ví dụ: nếu bạn muốn sử dụng nó để tiêm phụ thuộc (sử dụng tên của params) thì bạn có thể làm như sau:
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);
}
}
}
Nó không phải là trình phân giải tiên tiến nhất hiện có nhưng nó đưa ra ý tưởng về cách bạn có thể sử dụng Proxy để xử lý nó nếu bạn muốn sử dụng trình phân tích cú pháp args cho DI đơn giản. Tuy nhiên, có một cảnh báo nhỏ trong phương pháp này. Chúng ta cần sử dụng các bài tập hủy hoại thay vì các thông số thông thường. Khi chúng ta truyền vào proxy của trình tiêm, việc hủy bỏ cũng giống như gọi hàm getter trên đối tượng.
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));
Điều này xuất ra như sau:
{
"tweeter": {
"api": {
"client": {}
}
},
"timeline": {
"api": {
"client": {}
}
}
}
Nó nối dây toàn bộ ứng dụng. Điểm tốt nhất là ứng dụng này rất dễ kiểm tra (bạn chỉ có thể khởi tạo từng lớp và chuyển qua các bản mô phỏng / sơ khai / vv). Ngoài ra nếu bạn cần trao đổi thực hiện, bạn có thể làm điều đó từ một nơi duy nhất. Tất cả điều này là có thể vì các đối tượng Proxy Proxy.
Lưu ý: Có rất nhiều công việc cần phải được thực hiện để làm điều này trước khi nó sẵn sàng để sử dụng sản xuất nhưng nó đưa ra ý tưởng về việc nó sẽ trông như thế nào.
Câu trả lời hơi muộn nhưng nó có thể giúp những người khác đang nghĩ về điều tương tự. 👍