是否有一种方法可以动态地获取函数的函数参数名?
假设我的函数是这样的
function doSomething(param1, param2, .... paramN){
// fill an array with the parameter name and value
// some other code
}
现在,我如何从函数内部获得参数名称及其值的列表到数组中?
是否有一种方法可以动态地获取函数的函数参数名?
假设我的函数是这样的
function doSomething(param1, param2, .... paramN){
// fill an array with the parameter name and value
// some other code
}
现在,我如何从函数内部获得参数名称及其值的列表到数组中?
当前回答
我修改了来自AngularJS的版本,它实现了一个依赖注入机制,可以在没有Angular的情况下工作。我还更新了STRIP_COMMENTS正则表达式以配合ECMA6,因此它支持签名中的默认值等内容。
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; var FN_ARG_SPLIT = /,/; var FN_ARG = /^\s*(_?)(.+?)\1\s*$/; var STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg; function annotate(fn) { var $inject, fnText, argDecl, last; if (typeof fn == 'function') { if (!($inject = fn.$inject)) { $inject = []; fnText = fn.toString().replace(STRIP_COMMENTS, ''); argDecl = fnText.match(FN_ARGS); argDecl[1].split(FN_ARG_SPLIT).forEach(function(arg) { arg.replace(FN_ARG, function(all, underscore, name) { $inject.push(name); }); }); fn.$inject = $inject; } } else { throw Error("not a function") } return $inject; } console.log("function(a, b)",annotate(function(a, b) { console.log(a, b, c, d) })) console.log("function(a, b = 0, /*c,*/ d)",annotate(function(a, b = 0, /*c,*/ d) { console.log(a, b, c, d) })) annotate({})
其他回答
空格和注释不容易出错的解决方案是:
var fn = function(/* whoa) */ hi, you){};
fn.toString()
.replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg,'')
.match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1]
.split(/,/)
["hi", "you"]
这一点还没有提到,如果你使用Typescript,你可以在使用decorator时发出元数据,这将允许你获得参数类型。
元数据只有在类/函数/道具上有装饰器时才会被触发。 这与哪个装饰器无关。
可以通过在tsconfig.json中将emitDecoratorMetadata设置为true来启用该特性
{
"compilerOptions": {
"emitDecoratorMetadata": true
}
}
由于元数据仍然是一个早期的建议,所以必须安装或反射反射元数据包。getMetadata不会被定义。
npm install reflect-metadata
你可以这样使用它:
const AnyDecorator = () : MethodDecorator => {
return target => { }
}
class Person{
@AnyDecorator()
sayHello(other: Person){}
}
const instance = new Person();
// This returns: Function
const funcType = Reflect.getMetadata('design:type', instance.sayHello);
// Returns an array of types, here it would be: [Person]
const funcParams = Reflect.getMetadata('design:paramtypes', instance.sayHello);
例如,在Angular的新版本中,它被用来决定注入什么——> https://stackoverflow.com/a/53041387/1087372
我以前试过这样做,但从来没有找到一个实用的方法来完成它。我最终传递了一个对象,然后循环遍历它。
//define like
function test(args) {
for(var item in args) {
alert(item);
alert(args[item]);
}
}
//then used like
test({
name:"Joe",
age:40,
admin:bool
});
下面是来自AngularJS的代码,它在依赖注入机制中使用了这种技术。
以下是来自http://docs.angularjs.org/tutorial/step_05的解释
Angular的依赖注入器为你的控制器提供服务 当控制器被构造时。依赖注入器 负责创建服务可能的任何传递依赖项 拥有(服务通常依赖于其他服务)。 注意,参数的名称是重要的,因为注入器 使用这些来查找依赖项。
/**
* @ngdoc overview
* @name AUTO
* @description
*
* Implicit module which gets automatically added to each {@link AUTO.$injector $injector}.
*/
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
function annotate(fn) {
var $inject,
fnText,
argDecl,
last;
if (typeof fn == 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
arg.replace(FN_ARG, function(all, underscore, name){
$inject.push(name);
});
});
fn.$inject = $inject;
}
} else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn')
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
}
return $inject;
}
无论哪种解决方案,它都不能破坏奇怪的函数,其toString()看起来也很奇怪:
function ( A, b
,c ,d
){}
另外,为什么要使用复杂的正则表达式?可以这样做:
function getArguments(f) {
return f.toString().split(')',1)[0].replace(/\s/g,'').substr(9).split(',');
}
这适用于每个函数,唯一的正则表达式是删除空白,由于.split技巧,它甚至不处理整个字符串。