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

假设我的函数是这样的

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

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


当前回答

尝试手动:

function something(arg1, arg2) {
  console.log ( arg1 + arg2 );
}

其他回答

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

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(',')。

这很简单。

At the first there is a deprecated arguments.callee — a reference to called function. At the second if you have a reference to your function you can easily get their textual representation. At the third if you calling your function as constructor you can also have a link via yourObject.constructor. NB: The first solution deprecated so if you can't to not use it you must also think about your app architecture. If you don't need exact variable names just use inside a function internal variable arguments without any magic.

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee

它们都将调用toString并将其替换为re,这样我们就可以创建一个helper:

// getting names of declared parameters
var getFunctionParams = function (func) {
    return String(func).replace(/[^\(]+\(([^\)]*)\).*/m, '$1');
}

一些例子:

// Solution 1. deprecated! don't use it!
var myPrivateFunction = function SomeFuncName (foo, bar, buz) {
    console.log(getFunctionParams(arguments.callee));
};
myPrivateFunction (1, 2);

// Solution 2.
var myFunction = function someFunc (foo, bar, buz) {
    // some code
};
var params = getFunctionParams(myFunction);
console.log(params);

// Solution 3.
var cls = function SuperKewlClass (foo, bar, buz) {
    // some code
};
var inst = new cls();
var params = getFunctionParams(inst.constructor);
console.log(params);

享受与JS!

UPD:实际上杰克·艾伦提供了一个更好的解决方案。GJ杰克!

这里有一种方法:

// Utility function to extract arg name-value pairs
function getArgs(args) {
    var argsObj = {};

    var argList = /\(([^)]*)/.exec(args.callee)[1];
    var argCnt = 0;
    var tokens;
    var argRe = /\s*([^,]+)/g;

    while (tokens = argRe.exec(argList)) {
        argsObj[tokens[1]] = args[argCnt++];
    }

    return argsObj;
}

// Test subject
function add(number1, number2) {
    var args = getArgs(arguments);
    console.log(args); // ({ number1: 3, number2: 4 })
}

// Invoke test subject
add(3, 4);

注意:这只适用于支持arguments.callee的浏览器。

我不知道这个解决方案是否适合您的问题,但它允许您重新定义任何您想要的函数,而无需更改使用它的代码。现有调用将使用定位参数,而函数实现可能使用“命名参数”(单个散列参数)。

我认为你无论如何都会修改现有的函数定义,所以,为什么不拥有一个工厂函数,让你想要的东西:

<!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>

希望能有所帮助。

由于JavaScript是一种脚本语言,我认为它的内省应该支持获取函数参数名。在这个功能上下注违反了基本原则,所以我决定进一步探讨这个问题。

这让我想到了这个问题,但没有内置的解决方案。这让我得到了这个答案,它解释了参数只在函数之外被弃用,所以我们不能再使用myFunction。或者我们得到:

TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

是时候卷起袖子开始工作了:

⭐检索函数参数需要解析器,因为像4*(5/3)这样的复杂表达式可以用作默认值。所以Gaafar的答案或者James Drew的答案是目前为止最好的方法。

我尝试了巴比伦和esprima解析器,但不幸的是,它们不能解析独立的匿名函数,正如Mateusz Charytoniuk的回答所指出的那样。我想出了另一个解决办法,把代码括在括号里,这样就不会改变逻辑:

const ast = parser.parse("(\n" + func.toString() + "\n)")

换行符防止了//(单行注释)的问题。

⭐如果解析器不可用,次优选择是使用一种可靠的技术,比如Angular.js的依赖注入器正则表达式。我结合了Lambder的答案和humbletim的答案的函数版本,并添加了一个可选的ARROW布尔值,用于控制正则表达式是否允许ES6胖箭头函数。


这里有两个解决方案。注意,它们没有检测函数是否具有有效语法的逻辑,它们只提取参数。这通常是可以的,因为我们通常将解析函数传递给getArguments(),因此它们的语法已经有效。

我将尽我所能管理这些解决方案,但如果没有JavaScript维护者的努力,这将仍然是一个悬而未决的问题。

Node.js版本(在StackOverflow支持Node.js之前不能运行):

const parserName = 'babylon'; // const parserName = 'esprima'; const parser = require(parserName); function getArguments(func) { const maybe = function (x) { return x || {}; // optionals support } try { const ast = parser.parse("(\n" + func.toString() + "\n)"); const program = parserName == 'babylon' ? ast.program : ast; return program .body[0] .expression .params .map(function(node) { return node.name || maybe(node.left).name || '...' + maybe(node.argument).name; }); } catch (e) { return []; // could also return null } }; ////////// TESTS ////////// function logArgs(func) { let object = {}; object[func] = getArguments(func); console.log(object); // console.log(/*JSON.stringify(*/getArguments(func)/*)*/); } console.log(''); console.log('////////// MISC //////////'); logArgs((a, b) => {}); logArgs((a, b = 1) => {}); logArgs((a, b, ...args) => {}); logArgs(function(a, b, ...args) {}); logArgs(function(a, b = 1, c = 4 * (5 / 3), d = 2) {}); logArgs(async function(a, b, ...args) {}); logArgs(function async(a, b, ...args) {}); console.log(''); console.log('////////// FUNCTIONS //////////'); logArgs(function(a, b, c) {}); logArgs(function() {}); logArgs(function named(a, b, c) {}); logArgs(function(a /* = 1 */, b /* = true */) {}); logArgs(function fprintf(handle, fmt /*, ...*/) {}); logArgs(function(a, b = 1, c) {}); logArgs(function(a = 4 * (5 / 3), b) {}); // logArgs(function (a, // single-line comment xjunk) {}); // logArgs(function (a /* fooled you {}); // logArgs(function (a /* function() yes */, \n /* no, */b)/* omg! */ {}); // logArgs(function ( A, b \n,c ,d \n ) \n {}); logArgs(function(a, b) {}); logArgs(function $args(func) {}); logArgs(null); logArgs(function Object() {}); console.log(''); console.log('////////// STRINGS //////////'); logArgs('function (a,b,c) {}'); logArgs('function () {}'); logArgs('function named(a, b, c) {}'); logArgs('function (a /* = 1 */, b /* = true */) {}'); logArgs('function fprintf(handle, fmt /*, ...*/) {}'); logArgs('function( a, b = 1, c ) {}'); logArgs('function (a=4*(5/3), b) {}'); logArgs('function (a, // single-line comment xjunk) {}'); logArgs('function (a /* fooled you {}'); logArgs('function (a /* function() yes */, \n /* no, */b)/* omg! */ {}'); logArgs('function ( A, b \n,c ,d \n ) \n {}'); logArgs('function (a,b) {}'); logArgs('function $args(func) {}'); logArgs('null'); logArgs('function Object() {}');

完整的工作示例:

https://repl.it/repls/SandybrownPhonyAngles

浏览器版本(注意它停在第一个复杂的默认值):

function getArguments(func) { const ARROW = true; const FUNC_ARGS = ARROW ? /^(function)?\s*[^\(]*\(\s*([^\)]*)\)/m : /^(function)\s*[^\(]*\(\s*([^\)]*)\)/m; const FUNC_ARG_SPLIT = /,/; const FUNC_ARG = /^\s*(_?)(.+?)\1\s*$/; const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; return ((func || '').toString().replace(STRIP_COMMENTS, '').match(FUNC_ARGS) || ['', '', ''])[2] .split(FUNC_ARG_SPLIT) .map(function(arg) { return arg.replace(FUNC_ARG, function(all, underscore, name) { return name.split('=')[0].trim(); }); }) .filter(String); } ////////// TESTS ////////// function logArgs(func) { let object = {}; object[func] = getArguments(func); console.log(object); // console.log(/*JSON.stringify(*/getArguments(func)/*)*/); } console.log(''); console.log('////////// MISC //////////'); logArgs((a, b) => {}); logArgs((a, b = 1) => {}); logArgs((a, b, ...args) => {}); logArgs(function(a, b, ...args) {}); logArgs(function(a, b = 1, c = 4 * (5 / 3), d = 2) {}); logArgs(async function(a, b, ...args) {}); logArgs(function async(a, b, ...args) {}); console.log(''); console.log('////////// FUNCTIONS //////////'); logArgs(function(a, b, c) {}); logArgs(function() {}); logArgs(function named(a, b, c) {}); logArgs(function(a /* = 1 */, b /* = true */) {}); logArgs(function fprintf(handle, fmt /*, ...*/) {}); logArgs(function(a, b = 1, c) {}); logArgs(function(a = 4 * (5 / 3), b) {}); // logArgs(function (a, // single-line comment xjunk) {}); // logArgs(function (a /* fooled you {}); // logArgs(function (a /* function() yes */, \n /* no, */b)/* omg! */ {}); // logArgs(function ( A, b \n,c ,d \n ) \n {}); logArgs(function(a, b) {}); logArgs(function $args(func) {}); logArgs(null); logArgs(function Object() {}); console.log(''); console.log('////////// STRINGS //////////'); logArgs('function (a,b,c) {}'); logArgs('function () {}'); logArgs('function named(a, b, c) {}'); logArgs('function (a /* = 1 */, b /* = true */) {}'); logArgs('function fprintf(handle, fmt /*, ...*/) {}'); logArgs('function( a, b = 1, c ) {}'); logArgs('function (a=4*(5/3), b) {}'); logArgs('function (a, // single-line comment xjunk) {}'); logArgs('function (a /* fooled you {}'); logArgs('function (a /* function() yes */, \n /* no, */b)/* omg! */ {}'); logArgs('function ( A, b \n,c ,d \n ) \n {}'); logArgs('function (a,b) {}'); logArgs('function $args(func) {}'); logArgs('null'); logArgs('function Object() {}');

完整的工作示例:

https://repl.it/repls/StupendousShowyOffices