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

假设我的函数是这样的

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

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


当前回答

//See this:


// global var, naming bB
var bB = 5;

//  Dependency Injection cokntroller
var a = function(str, fn) {
  //stringify function body
  var fnStr = fn.toString();

  // Key: get form args to string
  var args = fnStr.match(/function\s*\((.*?)\)/);
  // 
  console.log(args);
  // if the form arg is 'bB', then exec it, otherwise, do nothing
  for (var i = 0; i < args.length; i++) {
    if(args[i] == 'bB') {
      fn(bB);
    }
  }
}
// will do nothing
a('sdfdfdfs,', function(some){
alert(some)
});
// will alert 5

a('sdfdsdsfdfsdfdsf,', function(bB){
alert(bB)
});

// see, this shows you how to get function args in string

其他回答

函数参数字符串值图像动态从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>

从@jack-allan中得到答案,我稍微修改了函数,以允许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;
    }
}

由于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

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

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

这里有一种方法:

// 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的浏览器。