是否有一种方法可以动态地获取函数的函数参数名?

假设我的函数是这样的

function doSomething(param1, param2, .... paramN){
   // fill an array with the parameter name and value
   // some other code 
}

现在,我如何从函数内部获得参数名称及其值的列表到数组中?


当前回答

这一点还没有提到,如果你使用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

其他回答

下面的函数将返回传入的任何函数的参数名称的数组。

var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
  var fnStr = func.toString().replace(STRIP_COMMENTS, '');
  var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
  if(result === null)
     result = [];
  return result;
}

使用示例:

getParamNames(getParamNames) // returns ['func']
getParamNames(function (a,b,c,d){}) // returns ['a','b','c','d']
getParamNames(function (a,/*b,c,*/d){}) // returns ['a','d']
getParamNames(function (){}) // returns []

编辑:

随着ES6的发明,这个函数可以被默认参数绊倒。这里有一个在大多数情况下都有效的快速技巧:

var STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg;

我说大多数情况是因为有些事情会出错

function (a=4*(5/3), b) {} // returns ['a']

编辑: 我还注意到vikasde也想要数组中的参数值。这已经在名为arguments的局部变量中提供了。

节选自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments:

arguments对象不是数组。它类似于Array,但除了长度之外没有任何Array属性。例如,它没有pop方法。然而,它可以转换为一个真正的数组:

var args = Array.prototype.slice.call(arguments);

如果Array泛型可用,则可以使用以下方法:

var args = Array.slice(arguments);

这一点还没有提到,如果你使用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

我已经阅读了这里的大部分答案,我想加上我的一句话。

new RegExp('(?:'+Function.name+'\\s*|^)\\((.*?)\\)').exec(Function.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '')

or

function getParameters(func) {
  return new RegExp('(?:'+func.name+'\\s*|^)\\s*\\((.*?)\\)').exec(func.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '');
}

或ECMA6中的单行函数

var getParameters = func => new RegExp('(?:'+func.name+'\\s*|^)\\s*\\((.*?)\\)').exec(func.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '');

__

假设有一个函数

function foo(abc, def, ghi, jkl) {
  //code
}

下面的代码将返回"abc,def,ghi,jkl"

该代码也将与Camilo Martin给出的一个函数的设置一起工作:

function  (  A,  b
,c      ,d
){}

还有Bubersson对Jack Allan的回答的评论:

function(a /* fooled you)*/,b){}

__

解释

新RegExp (' (?: + Function.name + \ \年代* | ^)\ \ s *\\((.*?)\\)')

这将创建一个正则表达式与新的正则表达式((?:' + Function.name + ' \ \ s * | ^) \ \ s *\\((.*?)\\)').我必须使用新的RegExp,因为我将一个变量(function .name,目标函数的名称)注入到RegExp中。

如果函数名是"foo" (function foo()), RegExp将是/foo\s*\((.*?)\)/。

Function.toString()。替换(\ n / g,”)

然后它将整个函数转换为一个字符串,并删除所有换行符。删除换行符有助于Camilo Martin给出的函数设置。

.exec(…)[1]

这是RegExp.prototype.exec函数。它基本上将正则指数(new RegExp())匹配到字符串(Function.toString())中。然后[1]将返回正则指数((.*?))中找到的第一个捕获组。

.replace (/ \ / \ * . * ?\ \ * / / g,”)。Replace (/ /g, ")

这将删除/*和*/内的所有注释,并删除所有空格。


这现在也支持读取和理解箭头(=>)函数,如f = (a, b) => void 0;,其中function . tostring()将返回(a, b) => void 0,而不是普通函数的函数f(a, b){返回void 0;}。原来的正则表达式会在混乱中抛出一个错误,但现在已经解决了这个问题。

的变化是新的正则表达式(Function.name + \ \年代 *\\((.*?)\\)') (/ 函数\ s *\((.*?)\)/) 新的正则表达式(’(?:+ Function.name + \ \年代 *|^)\\((.*?)\\)') (/(?: 函数\ s *|^)\((.*?)\)/)


如果你想让所有的参数变成一个数组,而不是一个由逗号分隔的字符串,在最后添加.split(',')。

这个问题的答案需要3个步骤:

To get the values of the actual parameters passed to the function (let's call it argValues). This is straight forward as it will be available as arguments inside the function. To get the parameter names from the function signature (let's call it argNames). This not as easy and requires parsing the function. Instead of doing the complex regex yourself and worrying about edge cases (default parameters, comments, ...), you can use a library like babylon that will parse the function into an abstract syntax tree from which you can obtain the names of parameters. The last step is to join the 2 arrays together into 1 array that has the name and value of all the parameters.

代码是这样的

const babylon = require("babylon")
function doSomething(a, b, c) {
    // get the values of passed argumenst
    const argValues = arguments

    // get the names of the arguments by parsing the function
    const ast = babylon.parse(doSomething.toString())
    const argNames =  ast.program.body[0].params.map(node => node.name)

    // join the 2 arrays, by looping over the longest of 2 arrays
    const maxLen = Math.max(argNames.length, argValues.length)
    const args = []
    for (i = 0; i < maxLen; i++) { 
       args.push({name: argNames[i], value: argValues[i]})
    }
    console.log(args)

    // implement the actual function here
}

doSomething(1, 2, 3, 4)

日志对象将为

[
  {
    "name": "a",
    "value": 1
  },
  {
    "name": "c",
    "value": 3
  },
  {
    "value": 4
  }
]

下面是一个工作示例https://tonicdev.com/5763eb77a945f41300f62a79/5763eb77a945f41300f62a7a

正确的方法是使用JS解析器。下面是一个使用橡子的例子。

const acorn = require('acorn');    

function f(a, b, c) {
   // ...
}

const argNames = acorn.parse(f).body[0].params.map(x => x.name);
console.log(argNames);  // Output: [ 'a', 'b', 'c' ]

这里的代码查找函数f的三个(形式)参数的名称。它通过将f输入acorn.parse()来实现。