12

Possible Duplicate:
How to get function parameter names/values dynamically from javascript

I'm currently working on a project in javascript (node.js) that has me trying to get an array of parameter names (NOT values, I do not need arguments) from a function. I'm currently using Function.toString() to get the function string and then running a regex against that to get my parameter list.

Let's take the following SIMPLE example:

var myFunction = function (paramOne, paramTwo) { ... }

Running my regex against this, and then doing some string magic (split, etc) I would expect an array back like this:

paramList = ['paramOne', 'paramTwo']

I have something that works but I'm feeling like it's probably not the best solution given some of the funky characters javascript lets you use for variable names and that javascript will let you define functions on multiple lines.

Here is what I currently have:

function.*[\w\s$]*(\((.*[\w\s,$]*)\))

This gives me my "match" in group 1 and then my param list without parens in group 2, which is cool. Is this really the best way to do what I want? Is there a better regular expression I could use for this? I'm not really looking for something "simpler" but really just something that could catch all possible situations.

Any help would be appreciated, and many thanks in advance!

0

3 Answers 3

25

Preface: By far, the best way to handle this is to use a JavaScript parser rather than trying to do it with a single regular expression. Regular expressions can be part of a parser, but no one regular expression can do the work of a parser. JavaScript's syntax (like that of most programming languages) is far too complex and context-sensitive to be handled with a simple regular expression or two. There are several open source JavaScript parsers written in JavaScript. I strongly recommend using one of those, not what's below.


The easiest thing would be to capture everything in the first set of parens, and then use split(/\s*,\s*/) to get the array.

E.g.:

var str = "function(   one  ,\ntwo,three   ,   four   ) { laksjdfl akjsdflkasjdfl }";
var args = /\(\s*([^)]+?)\s*\)/.exec(str);
if (args[1]) {
  args = args[1].split(/\s*,\s*/);
}
console.log("args: ", args);

How the above works:

  1. We use /\( *([^)]+?) *\)/ to match the first opening parenthesis (\( since ( is special in regexes), followed by any amount of optional whitespace, followed by a capture group capturing everything but a closing parenthesis (but non-greedy), followed by any amount of optional whitespace, followed by the closing ).

  2. If we succeed, we split using /\s*,\s*/, which means we split on sequences which are zero or more whitespace characters (\s*) followed by a comma followed by zero or more whitespace characters (this whitespace thing is why the args in my example function are so weird).

As you can see from the example, this handles leading whitespace (after the ( and before the first argument), whitespace around the commas, and trailing whitespace — including line breaks. It does not try to handle comments within the argument list, which would markedly complicate things.

Note: The above doesn't handle ES2015's default parameter values, which can be any arbitrary expression, including an expression containing a ) — which breaks the regex above by stopping its search early:

var str = "function(   one  ,\ntwo = getDefaultForTwo(),three   ,   four   ) { laksjdfl akjsdflkasjdfl }";
var args = /\(\s*([^)]+?)\s*\)/.exec(str);
if (args[1]) {
  args = args[1].split(/\s*,\s*/);
}
console.log("args: ", args);

Which brings us full circle to: Use a JavaScript parser. :-)

Sign up to request clarification or add additional context in comments.

10 Comments

How do you think I should go about that? That's what I'm trying to do with my regex, but maybe there is a different approach? Are you thinking I could just indexOf "(" and ")" and then split what's in between? Would that be better than a crazy regex?
@JasonL.: I added some code to the answer (and a live example).
Thanks! Your solution is much more simpler than I think I was expecting. I'm currently trying it out with all the different variations javascript allows for. Will this capture any of the crazy special characters javascript allows (one of the most famous being the look of disapproval)? I should also note that this is for the node.js environment ONLY, so I really only need to worry about V8 :)
@JasonL.: The key bits in the above is are 1) the contents of the capture group in the first regular expression, [^)]+, which means "one or more characters that aren't a closing parenthesis", and 2) the regular expression in the split /\s*,\s*/, which splits on sequences of zero-or-more whitespace chars followed by a comma followed by zero-or-more whitespace chars. So you should be fine with all of the weird and wonderful valid identifiers. It's comments you have to worry about. :-)
Ahh, good point. And it looks like V8 does include comments. I suppose I should look into the source you linked and see how they ignore the comments :)
|
4

Do as following:

var ar = str.match(/\((.*?)\)/);
if (ar) {
  var result = ar[0].split(",");
}

Remember ? after * does a non greedy find

1 Comment

/(?<=\().*?(?=\))/ startswith ( (?<=\() content (?=\)) endswith )
2

Let me suggest you using regular expressions:

  • [match] /function[^(]*\(([^)]*)\)/ will match the argument list
  • [split] /\W+/ (against the results of the first match data) will split the match into params list

So, the code should look like this:

var s = "function moo (paramOne, paramTwo) { alert('hello'); }";
var s2 = s.match(/function[^(]*\(([^)]*)\)/)[1];
var paramList = s2.split(/\W+/);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.