Javascript 如何动态获取函数参数名称/值?

Javascript 如何动态获取函数参数名称/值?,javascript,reflection,function-parameter,Javascript,Reflection,Function Parameter,有没有办法动态获取函数的函数参数名 假设我的函数如下所示: function doSomething(param1, param2, .... paramN){ // fill an array with the parameter name and value // some other code } getParameters(function (hello /*, foo ),* /bar* { */,world) {}); // ["hello", "world"] 现

有没有办法动态获取函数的函数参数名

假设我的函数如下所示:

function doSomething(param1, param2, .... paramN){
   // fill an array with the parameter name and value
   // some other code 
}
getParameters(function (hello /*, foo ),* /bar* { */,world) {}); // ["hello", "world"]

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

我不知道如何获取参数列表,但您可以这样做来获取它期望的数量

alert(doSomething.length);

我以前试过这样做,但从来没有找到一个实用的方法来完成它。我最终传入了一个对象,然后在其中循环

//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
});

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

我认为您无论如何都会修改现有的函数定义,因此,为什么不使用一个工厂函数来实现您想要的:

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

var withNamedParams=函数(参数,λ){
返回函数(){
var name={};
var max=arguments.length;

对于(var i=0;i,可以使用“arguments”属性访问传递给函数的参数值

    function doSomething()
    {
        var args = doSomething.arguments;
        var numArgs = args.length;
        for(var i = 0 ; i < numArgs ; i++)
        {
            console.log("arg " + (i+1) + " = " + args[i]);  
                    //console.log works with firefox + firebug
                    // you can use an alert to check in other browsers
        }
    }

    doSomething(1, '2', {A:2}, [1,2,3]);    
函数doSomething()
{
var args=doSomething.arguments;
var numArgs=args.length;
对于(变量i=0;i
这里有一种方法:

// 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);

注意:这仅适用于支持
参数的浏览器。被调用方

以下函数将返回传入的任何函数的参数名数组

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的局部变量中提供

摘录自:

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

var args = Array.prototype.slice.call(arguments);
如果阵列泛型可用,则可以使用以下方法:

var args = Array.slice(arguments);
我通常如何做:

function name(arg1, arg2){
    var args = arguments; // array: [arg1, arg2]
    var objecArgOne = args[0].one;
}
name({one: "1", two: "2"}, "string");
您甚至可以通过函数名引用参数,如:

name.arguments;

希望这能有所帮助!

下面的代码取自AngularJS,它将该技术用于依赖项注入机制

下面是一个来自

Angular的依赖项注入器为控制器提供服务 当构造控制器时。依赖关系注入器也 负责创建服务可能存在的任何可传递依赖项 have(服务通常依赖于其他服务)

请注意,参数的名称很重要,因为 使用这些来查找依赖项

//请参见:
//全局变量,命名为bB
var-bB=5;
//依赖注入控制器
变量a=函数(str,fn){
//弦化函数体
var fnStr=fn.toString();
//键:将表单参数获取为字符串
var args=fnStr.match(/function\s*\(.*?)/);
// 
console.log(args);
//如果表单arg为“bB”,则执行它,否则,不执行任何操作
对于(变量i=0;i
这很简单

首先是一个弃用的
参数。被调用方
——对被调用函数的引用。 第二步,如果你有一个函数的引用,你可以很容易地得到它们的文本表示。 第三步,如果您以构造函数的形式调用函数,还可以通过yourObject.constructor创建一个链接。 注意:第一个解决方案已被弃用,因此如果您不能不使用它,您还必须考虑您的应用程序架构。 如果不需要精确的变量名,只需在函数内部使用内部变量
参数
,无需任何魔法

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

// 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:Jack Allan实际上得到了一个更好的解决方案。GJ Jack!

不太容易出现空格和注释错误的解决方案是:

var fn = function(/* whoa) */ hi, you){};

fn.toString()
  .replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg,'')
  .match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1]
  .split(/,/)

["hi", "you"]
=>[“a”、“b”、“c”]

您还可以使用“esprima”解析器来避免参数列表中的注释、空格和其他问题

function getParameters(yourFunction) {
    var i,
        // safetyValve is necessary, because sole "function () {...}"
        // is not a valid syntax
        parsed = esprima.parse("safetyValve = " + yourFunction.toString()),
        params = parsed.body[0].expression.right.params,
        ret = [];

    for (i = 0; i < params.length; i += 1) {
        // Handle default params. Exe: function defaults(a = 0,b = 2,c = 3){}
        if (params[i].type == 'AssignmentPattern') {
            ret.push(params[i].left.name)
        } else {
            ret.push(params[i].name);
        }
    }

    return ret;
}
从@jack allan I获得的命令稍微修改了该函数,以允许ES6默认属性,例如:

function( a, b = 1, c ){};
仍然返回
['a','b']

/**
 * Get the keys of the paramaters of a function.
 *
 * @param {function} method  Function to get parameter keys for
 * @return {array}
 */
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /(?:^|,)\s*([^\s,=]+)/g;
function getFunctionParameters ( func ) {
    var fnStr = func.toString().replace(STRIP_COMMENTS, '');
    var argsList = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')'));
    var result = argsList.match( ARGUMENT_NAMES );

    if(result === null) {
        return [];
    }
    else {
        var stripped = [];
        for ( var i = 0; i < result.length; i++  ) {
            stripped.push( result[i].replace(/[\s,]/g, '') );
        }
        return stripped;
    }
}
/**
*获取函数参数的键。
*
*@param{function}方法函数以获取
*@return{array}
*/
var STRIP\u COMMENTS=/(\/\/.$)|(\/\*[\s\s]*?\*\/)/mg;
变量参数名称=/(?:^ |,)\s*([^\s,=]+)/g;
函数getFunctionParameters(func){
var fnStr=func.toString().replace(带注释“”);
var argsList=fnStr.slice(fnStr.indexOf('(')+1,fnStr.indexOf('));
var result=argsList.match(参数名称);
如果(结果===null){
返回[];
}
否则{
var=[];
对于(变量i=0;i
无论采用何种解决方案,它都不能破坏wierd函数,其
toString()
看起来与wierd一样:

function  (  A,  b
,c      ,d
){}
getParameters(function (hello /*, foo ),* /bar* { */,world) {}); // ["hello", "world"]
function( a, b = 1, c ){};
/**
 * Get the keys of the paramaters of a function.
 *
 * @param {function} method  Function to get parameter keys for
 * @return {array}
 */
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /(?:^|,)\s*([^\s,=]+)/g;
function getFunctionParameters ( func ) {
    var fnStr = func.toString().replace(STRIP_COMMENTS, '');
    var argsList = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')'));
    var result = argsList.match( ARGUMENT_NAMES );

    if(result === null) {
        return [];
    }
    else {
        var stripped = [];
        for ( var i = 0; i < result.length; i++  ) {
            stripped.push( result[i].replace(/[\s,]/g, '') );
        }
        return stripped;
    }
}
function  (  A,  b
,c      ,d
){}
function getArguments(f) {
    return f.toString().split(')',1)[0].replace(/\s/g,'').substr(9).split(',');
}
function $args(func) {  
    return (func + '')
      .replace(/[/][/].*$/mg,'') // strip single-line comments
      .replace(/\s+/g, '') // strip white space
      .replace(/[/][*][^/*]*[*][/]/g, '') // strip multi-line comments  
      .split('){', 1)[0].replace(/^[^(]*[(]/, '') // extract the parameters  
      .replace(/=[^,]+/g, '') // strip any ES6 defaults  
      .split(',').filter(Boolean); // split & filter [""]
}  
'function (a,b,c)...' // returns ["a","b","c"]
'function ()...' // returns []
'function named(a, b, c) ...' // returns ["a","b","c"]
'function (a /* = 1 */, b /* = true */) ...' // returns ["a","b"]
'function fprintf(handle, fmt /*, ...*/) ...' // returns ["handle","fmt"]
'function( a, b = 1, c )...' // returns ["a","b","c"]
'function (a=4*(5/3), b) ...' // returns ["a","b"]
'function (a, // single-line comment xjunk) ...' // returns ["a","b"]
'function (a /* fooled you...' // returns ["a","b"]
'function (a /* function() yes */, \n /* no, */b)/* omg! */...' // returns ["a","b"]
'function ( A, b \n,c ,d \n ) \n ...' // returns ["A","b","c","d"]
'function (a,b)...' // returns ["a","b"]
'function $args(func) ...' // returns ["func"]
'null...' // returns ["null"]
'function Object() ...' // returns []
function doSomething(foo, bar) {
    console.log("does something");
}
function doSomething() {
    var foo = arguments[0];
    var bar = arguments[1];

    console.log("does something");
}
function saySomething(obj) {
  if(obj.message) console.log((obj.sender || "Anon") + ": " + obj.message);
}

saySomething({sender: "user123", message: "Hello world"});
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
  }
]
    function do_tests(func) {

    if (typeof func !== 'function') return true;
    switch (typeof func.tests) {
        case 'undefined' : return true;
        case 'object'    : 
            for (var k in func.tests) {

                var test = func.tests[k];
                if (typeof test==='function') {
                    var result = test(func);
                    if (result===false) {
                        console.log(test.name,'for',func.name,'failed');
                        return false;
                    }
                }

            }
            return true;
        case 'function'  : 
            return func.tests(func);
    }
    return true;
} 
function strip_comments(src) {

    var spaces=(s)=>{
        switch (s) {
            case 0 : return '';
            case 1 : return ' ';
            case 2 : return '  ';
        default : 
            return Array(s+1).join(' ');
        }
    };

    var c1 = src.indexOf ('/*'),
        c2 = src.indexOf ('//'),
        eol;

    var out = "";

    var killc2 = () => {
                out += src.substr(0,c2);
                eol =  src.indexOf('\n',c2);
                if (eol>=0) {
                    src = spaces(eol-c2)+'\n'+src.substr(eol+1);
                } else {
                    src = spaces(src.length-c2);
                    return true;
                }

             return false;
         };

    while ((c1>=0) || (c2>=0)) {
         if (c1>=0) {
             // c1 is a hit
             if ( (c1<c2) || (c2<0) )  {
                 // and it beats c2
                 out += src.substr(0,c1);
                 eol = src.indexOf('*/',c1+2);
                 if (eol>=0) {
                      src = spaces((eol-c1)+2)+src.substr(eol+2);
                 } else {
                      src = spaces(src.length-c1);
                      break;
                 }
             } else {

                 if (c2 >=0) {
                     // c2 is a hit and it beats c1
                     if (killc2()) break;
                 }
             }
         } else {
             if (c2>=0) {
                // c2 is a hit, c1 is a miss.
                if (killc2()) break;  
             } else {
                 // both c1 & c2 are a miss
                 break;
             }
         }

         c1 = src.indexOf ('/*');
         c2 = src.indexOf ('//');   
        }

    return out + src;
}

function function_args(fn) {
    var src = strip_comments(fn.toString());
    var names=src.split(')')[0].replace(/\s/g,'').split('(')[1].split(',');
    return names;
}

function_args.tests = [

     function test1 () {

            function/*al programmers will sometimes*/strip_comments_tester/* because some comments are annoying*/(
            /*see this---(((*/ src//)) it's an annoying comment does not help anyone understand if the 
            ,code,//really does
            /**/sucks ,much /*?*/)/*who would put "comment\" about a function like (this) { comment } here?*/{

            }


        var data = function_args(strip_comments_tester);

        return ( (data.length==4) &&
                 (data[0]=='src') &&
                 (data[1]=='code') &&
                 (data[2]=='sucks') &&
                 (data[3]=='much')  );

    }

];
do_tests(function_args);
new RegExp('(?:'+Function.name+'\\s*|^)\\((.*?)\\)').exec(Function.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '')
function getParameters(func) {
  return new RegExp('(?:'+func.name+'\\s*|^)\\s*\\((.*?)\\)').exec(func.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '');
}
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
}
function  (  A,  b
,c      ,d
){}
function(a /* fooled you)*/,b){}
function test(arg1,arg2){
    var funcStr = test.toString()
    var leftIndex = funcStr.indexOf('(');
    var rightIndex = funcStr.indexOf(')');
    var paramStr = funcStr.substr(leftIndex+1,rightIndex-leftIndex-1);
    var params = paramStr.split(',');
    for(param of params){
        console.log(param);   // arg1,arg2
    }
}

test();
if (result[0] === '{' && result[result.length - 1 === '}']) result = result.slice(1, -1)
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
const ast = parser.parse("(\n" + func.toString() + "\n)")
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' ]
npm install reflect-metadata
const AnyDecorator = () : MethodDecorator => {
    return target => { }
}

class Person{
    @AnyDecorator()
    sayHello(other: Person){}
}
const instance = new Person();
const funcType = Reflect.getMetadata('design:type', instance.sayHello);
const funcParams = Reflect.getMetadata('design:paramtypes', instance.sayHello);