是否有一种方法可以动态地获取函数的函数参数名?
假设我的函数是这样的
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
}
现在,我如何从函数内部获得参数名称及其值的列表到数组中?
当前回答
(function(a,b,c){}).toString().replace(/.*\(|\).*/ig,"").split(',')
=> ["a", "b", "c"]
其他回答
哇,已经有这么多答案了。我很确定这件事会被埋葬。即便如此,我觉得这对一些人来说可能有用。
我对所选的答案并不完全满意,因为在ES6中,默认值不能很好地工作。它也不提供默认值信息。我还想要一个不依赖于外部库的轻量级函数。
这个函数对于调试非常有用,例如:记录被调用函数的参数、默认参数值和参数。
我昨天花了一些时间在这个问题上,破解正确的RegExp来解决这个问题,这就是我想到的。它运行得非常好,我对结果非常满意:
const REGEX_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; const REGEX_FUNCTION_PARAMS = /(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m const REGEX_PARAMETERS_VALUES = /\s*(\w+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm /** * Retrieve a function's parameter names and default values * Notes: * - parameters with default values will not show up in transpiler code (Babel) because the parameter is removed from the function. * - does NOT support inline arrow functions as default values * to clarify: ( name = "string", add = defaultAddFunction ) - is ok * ( name = "string", add = ( a )=> a + 1 ) - is NOT ok * - does NOT support default string value that are appended with a non-standard ( word characters or $ ) variable name * to clarify: ( name = "string" + b ) - is ok * ( name = "string" + $b ) - is ok * ( name = "string" + b + "!" ) - is ok * ( name = "string" + λ ) - is NOT ok * @param {function} func * @returns {Array} - An array of the given function's parameter [key, default value] pairs. */ function getParams(func) { let functionAsString = func.toString() let params = [] let match functionAsString = functionAsString.replace(REGEX_COMMENTS, '') functionAsString = functionAsString.match(REGEX_FUNCTION_PARAMS)[1] if (functionAsString.charAt(0) === '(') functionAsString = functionAsString.slice(1, -1) while (match = REGEX_PARAMETERS_VALUES.exec(functionAsString)) params.push([match[1], match[2]]) return params } // Lets run some tests! var defaultName = 'some name' function test1(param1, param2, param3) { return (param1) => param1 + param2 + param3 } function test2(param1, param2 = 4 * (5 / 3), param3) {} function test3(param1, param2 = "/root/" + defaultName + ".jpeg", param3) {} function test4(param1, param2 = (a) => a + 1) {} console.log(getParams(test1)) console.log(getParams(test2)) console.log(getParams(test3)) console.log(getParams(test4)) // [ [ 'param1', undefined ], [ 'param2', undefined ], [ 'param3', undefined ] ] // [ [ 'param1', undefined ], [ 'param2', '4 * (5 / 3)' ], [ 'param3', undefined ] ] // [ [ 'param1', undefined ], [ 'param2', '"/root/" + defaultName + ".jpeg"' ], [ 'param3', undefined ] ] // [ [ 'param1', undefined ], [ 'param2', '( a' ] ] // --> This last one fails because of the inlined arrow function! var arrowTest1 = (a = 1) => a + 4 var arrowTest2 = a => b => a + b var arrowTest3 = (param1 = "/" + defaultName) => { return param1 + '...' } var arrowTest4 = (param1 = "/" + defaultName, param2 = 4, param3 = null) => { () => param3 ? param3 : param2 } console.log(getParams(arrowTest1)) console.log(getParams(arrowTest2)) console.log(getParams(arrowTest3)) console.log(getParams(arrowTest4)) // [ [ 'a', '1' ] ] // [ [ 'a', undefined ] ] // [ [ 'param1', '"/" + defaultName' ] ] // [ [ 'param1', '"/" + defaultName' ], [ 'param2', '4' ], [ 'param3', 'null' ] ] console.log(getParams((param1) => param1 + 1)) console.log(getParams((param1 = 'default') => { return param1 + '.jpeg' })) // [ [ 'param1', undefined ] ] // [ [ 'param1', '\'default\'' ] ]
正如你所知道的,一些参数名消失了,因为Babel转译器将它们从函数中删除了。如果你在最新的NodeJS中运行它,它会像预期的那样工作(注释的结果来自NodeJS)。
另一个注意事项,正如注释中所述,它不能作为默认值与内联箭头函数一起工作。这使得使用RegExp提取值变得非常复杂。
如果这对你有用,请让我知道!很乐意听到一些反馈!
注意:如果你想使用ES6参数解构与顶部解决方案添加下面的行。
if (result[0] === '{' && result[result.length - 1 === '}']) result = result.slice(1, -1)
这一点还没有提到,如果你使用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
我通常是怎么做的:
function name(arg1, arg2){
var args = arguments; // array: [arg1, arg2]
var objecArgOne = args[0].one;
}
name({one: "1", two: "2"}, "string");
你甚至可以通过函数名引用参数,比如:
name.arguments;
希望这能有所帮助!
我不知道这个解决方案是否适合您的问题,但它允许您重新定义任何您想要的函数,而无需更改使用它的代码。现有调用将使用定位参数,而函数实现可能使用“命名参数”(单个散列参数)。
我认为你无论如何都会修改现有的函数定义,所以,为什么不拥有一个工厂函数,让你想要的东西:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
var withNamedParams = function(params, lambda) {
return function() {
var named = {};
var max = arguments.length;
for (var i=0; i<max; i++) {
named[params[i]] = arguments[i];
}
return lambda(named);
};
};
var foo = withNamedParams(["a", "b", "c"], function(params) {
for (var param in params) {
alert(param + ": " + params[param]);
}
});
foo(1, 2, 3);
</script>
</head>
<body>
</body>
</html>
希望能有所帮助。