6

can anyone help me on how to retrieve function parameter names? For example:

var A = function(a, b, c) {};

I need to get the parameter names outside the function, which is a, b, and c (as in literally as strings, such as to be put in an array ["a", "b", "c"]), so I can validate not only object/class properties in a class towards an interface, but also the parameters.


Before this question starts raising confusion, let me explain about interface implementation in my term and the reason why I need the answer of the question above:

First of all, I found one interesting way I would like to use in implementing interface by Glen Ford in JavaScript:

for (var member in theInterface) {
    if ( (typeof theObject[member] != typeof theInterface[member]) ) {
        alert("object failed to implement interface member " + member);
        return false;
    }
}

(Please see the detail about the implementation from http://knol.google.com/k/programming-to-the-interface-in-javascript-yes-it-can-be-done-er-i-mean-faked#).

However, the implementation above only validates the object properties (variable and methods) only, which is not enough for me.

I need more validation to parameter level, so that the code that implements one interface should follow the interface methods and also follow the parameters from the interface methods as well.


PS. Please don't argue about why use interface or why it's useless or useful; instead it's most useful if you can provide better interface implementation. The focus here is I just need to know how to get function parameter names.

  • This is an unclear question... what is a "parameter name"? You already declare your parameters to your function as "a" "b" and "c"... Can you clarify? Also, what do you mean by "validation"? Are you just looking for a way to validate the incoming parameters in multiple classes? If so, my first inclination would be to create a superclass or something to do the validation for all subclasses... – Jesse Fulton Feb 01 '12 at 06:47
  • Basically, JavaScript isn't type-safe - but it sounds like that's what you want? – Jesse Fulton Feb 01 '12 at 06:48
  • Hi Jesse, yes I've declared my parameters in the function as a, b, and c. But I need to get them (as in, literally take them as string, like ["a", "b", "c"]) so I can use these names for a further interface implementations. I need to "validate" my object properties to be the same as the interface object properties. If you've tried the interface implementation in the link I've provided, maybe you'll get the sense of what I mean. In the link, it only "validates" the object members towards the interface members, however, I need deeper, which also towards the interface members' parameter as well. –  Feb 01 '12 at 07:01
  • 1
    Possible duplicate: http://stackoverflow.com/questions/1007981/how-to-get-function-parameter-names-values-dynamically-from-javascript – Anderson Green Apr 24 '13 at 16:57

6 Answers6

12

I doubt that there is any decent way to this, in javascript parameter values can be of different type when passed, the types doesn't matter but the order thus.

This might not be the answer that you are looking for but this could help at least.

function test(a,b,c,d) {

}

//this will give us the number of parameters defined in the a function.
confirm(test.length); 

To really know the names you can parse the function definition

  var str = window['test'].toString();
   //str will be "function test(a,b,c,d){}"

Below is a simple parsing test I made:

function test(a,b,c,d)
{

}

var b = function(x,y,  z){};
alert(getFnParamNames(b).join(","));
alert(getFnParamNames(test).join(","));

function getFnParamNames(fn){
    var fstr = fn.toString();
    return fstr.match(/\(.*?\)/)[0].replace(/[()]/gi,'').replace(/\s/gi,'').split(',');
}
jerjer
  • 8,694
  • 30
  • 36
  • Thanks jerjer, I think this goes as a vote for parsing the function as a string.. –  Feb 01 '12 at 07:15
  • Yes, I think toString() would be the only way to figure out the parameter names. Then using a simple RegEx should allow you to map them to object properties. – Jesse Fulton Feb 01 '12 at 07:23
  • Thanks Jesse, RegEx would do! :) –  Feb 01 '12 at 07:30
  • @Arly, please see updated answer for a test function to parse the parameter names – jerjer Feb 01 '12 at 07:34
  • Hi jerjer, Glad you updated a better answer. I've tested it, and it seems like you're also trimming the strings so it doesn't save any white spaces caused by the function declarations (both style), which is very helpful if anyone see this. Nice! –  Feb 01 '12 at 07:56
  • AngularJS uses fn.toString() as jerjer suggested. Code: https://github.com/angular/angular.js/blob/29a05984fe46c2c18ca51404f07c866dd92d1eec/src/auto/injector.js#L73 – Max Nov 04 '15 at 16:04
  • We may also ensure this is at least one match! (see answer below) – Pooya Jul 17 '17 at 21:59
2

The only way would be to retrieve the function as a string and use a RegExp to get the parameter names:

function test(a,b,c){/*function body*/}
//=> this should return 'a,b,c':
test.toString().replace(/(function.+\()(.+(?=\)))(.+$)/,'$2');
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • Thanks! Works like a charm, just like what I needed when it comes to parsing it to a string. –  Feb 01 '12 at 07:34
  • Hi Arly, glad I could help. If you don't want to use `$2` (is deprecated) the alternative could be `test.toString().match(/(function.+\()(.+(?=\)))(.+$)/)[2];`. Happy coding! – KooiInc Feb 01 '12 at 07:53
0

You can use either a regex such as described in here, another alternative would be to use a tokenizer. The following code uses the acorn javascript parser for checking if a function contains a specific argument name:

function isFunction(value) {
  return value && typeof value === 'function';
}

function hasArgument(func, name) {

if (!isFunction(func) || !name) {
    return false;
}

var tokenizer = acorn.tokenize(func);
var token = tokenizer();

while (token.type.type !== 'eof') {

    // ')' closes function, we do not want to look inside the function body
    if (token.type.type === ')') {
        return false;
    }

    if (token.type.type === 'name' && token.value === name) {
        return true;
    }

    token = tokenizer();

}

return false;

}

Rui Ferrão
  • 199
  • 1
  • 9
0

Just to make complete @jerjer answer, We may also ensure there is at least one match:

function getFnParamNames(fn){
  const match = fn.toString().match(/\(.*?\)/) 
  return match ? match[0].replace(/[()]/gi,'').replace(/\s/gi,'').split(',') : [];
}
Pooya
  • 862
  • 7
  • 10
0

Now it's 2018 so may I add a solution that supports arrow function

/**
 *  (Function) => String[]
 */
function getFnParamNames(fn){
    const fnStr = fn.toString()
    const arrowMatch = fnStr.match(/\(?[^]*?\)?\s*=>/) 
    if (arrowMatch) return arrowMatch[0].replace(/[()\s]/gi,'').replace('=>','').split(',')
    const match = fnStr.match(/\([^]*?\)/) 
    return match ? match[0].replace(/[()\s]/gi,'').split(',') : []
}

Note:

[^] matches all kinds of characters including new line (while . excludes new line). Source RegExp Documentation

*? non-greedy quantifier. More explanation here

Tested cases:

console.log(getFnParamNames(name => {
}))

console.log(getFnParamNames(name=>{
}))

console.log(getFnParamNames((name) => {
}))

console.log(getFnParamNames((name, age, ...args) => {
}))

console.log(getFnParamNames((name,age,...args)=>{
}))

console.log(getFnParamNames(function(name,age,...args){
}))

console.log(getFnParamNames(function (name , age , ...args ) {
}))

console.log(getFnParamNames(function(
    name,
    age,
    ...args){
}))

console.log(getFnParamNames(new Function('name', 'age' , '...args', 'console.log(args.length)' )))
hirikarate
  • 3,055
  • 4
  • 21
  • 27
0

Actually, regex is not sufficient and robust enough for this task, the easiest way is using a library such as @plumier/reflect.

https://www.npmjs.com/package/@plumier/reflect

Its a mature reflection library currently used by several library and frameworks and some companies.

ktutnik
  • 6,882
  • 1
  • 29
  • 34