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

假设我的函数是这样的

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

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


当前回答

好了,这是个老问题,有很多足够的答案。 下面是我提供的不使用正则表达式的产品,除了去除空白的琐碎任务。(我应该注意到,“strips_comments”函数实际上是将它们分隔开来,而不是从物理上删除它们。这是因为我在其他地方使用它,由于各种原因需要原始非注释令牌的位置保持完整)

这是一个相当长的代码块,因为这个粘贴包含一个迷你测试框架。

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

其他回答

无论哪种解决方案,它都不能破坏奇怪的函数,其toString()看起来也很奇怪:

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

另外,为什么要使用复杂的正则表达式?可以这样做:

function getArguments(f) {
    return f.toString().split(')',1)[0].replace(/\s/g,'').substr(9).split(',');
}

这适用于每个函数,唯一的正则表达式是删除空白,由于.split技巧,它甚至不处理整个字符串。

哇,已经有这么多答案了。我很确定这件事会被埋葬。即便如此,我觉得这对一些人来说可能有用。

我对所选的答案并不完全满意,因为在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提取值变得非常复杂。

如果这对你有用,请让我知道!很乐意听到一些反馈!

函数参数字符串值图像动态从JSON。因为项目。product_image2是一个URL字符串,当你在参数内部调用changeImage时,你需要把它放在引号中。

我的功能

items+='<img src='+item.product_image1+' id="saleDetailDivGetImg">';
items+="<img src="+item.product_image2+"  onclick='changeImage(\""+item.product_image2+"\");'>";

我的函数

<script type="text/javascript">
function changeImage(img)
 {
    document.getElementById("saleDetailDivGetImg").src=img;
    alert(img);
}
</script>

我想建议解决方案,支持箭头函数像 我将本文用于基本正则表达式和https://davidwalsh.name/javascript-arguments,并添加了箭头函数支持

(arg1,arg2) => {}

or

arg => {}



function getArgs(func) {
  if(func.length === 0){
      return []
  }

  let string = func.toString();

  let args;
  // First match everything inside the function argument parens. like `function (arg1,arg2) {}` or `async function(arg1,arg2) {}


  args = string.match(/(?:async|function)\s*.*?\(([^)]*)\)/)?.[1] ||
      // arrow functions with multiple arguments  like `(arg1,arg2) => {}`
         string.match(/^\s*\(([^)]*)\)\s*=>/)?.[1] ||
      // arrow functions with single argument without parens like `arg => {}`
         string.match(/^\s*([^=]*)=>/)?.[1]

  // Split the arguments string into an array comma delimited.
  return args.split(',').map(function(arg) {
    // Ensure no inline comments are parsed and trim the whitespace.
    return arg.replace(/\/\*.*\*\//, '').trim();
  }).filter(function(arg) {
    // Ensure no undefined values are added.
    return arg;
  });
}

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

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