9

Is there a regular expression that matches JavaScript functions?

Like

function abc(var1, var2){
  ..
}

// and

abc : function(var1, var2){
  ...
},
Wyck
  • 10,311
  • 6
  • 39
  • 60
complez
  • 7,882
  • 11
  • 48
  • 56

5 Answers5

12

I know this question is 5 years old, but contrary to what everyone else has said, i have actually concocted a quite effective pattern for doing as you have asked. Albeit, quite complex, I have used this several times in my own projects and I've yet to have a hiccup... wish I had seen this question much sooner. Hope this helps (If not for you, hopefully for those who are searching for a similar solution)

function\s*([A-z0-9]+)?\s*\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)\s*\{(?:[^}{]+|\{(?:[^}{]+|\{[^}{]*\})*\})*\}

Ja Superior
  • 459
  • 4
  • 17
  • 2
    Cute, but here is a simple example that is not parsed correctly: function() { return "}" } – Zsolt Szatmari Mar 24 '17 at 07:25
  • @ZsoltSzatmari common man - whilst it's valid, not sure why you'd ever want that as a function... – Shannon Hochkins Oct 07 '19 at 23:51
  • Here's another simple example where it doesn't work: `function() {if(A){}else{if(B){if(C){}}}}` – WhiteAngel Dec 13 '21 at 13:11
  • Its true that the examples provided will result in a failed state. However, to correct for this, one simply has to account for more possiblities for nested curly braces (albeit tedious) in the latter example @whiteangel provided or in the former suggestion, add a greedy match to quoted matches. i.e. `".*"` again, tedious but could work. I think as someone suggested regular expressions arent the best way to handle this, but if you provide some linting rules to your application, it could :shrugs: – Ja Superior Apr 26 '22 at 22:07
  • Another example: `functiontest(){}` – Nightfall Feb 08 '23 at 20:30
  • well that function isnt even valid. – Ja Superior Feb 08 '23 at 22:24
4

In JS, a function can contain functions (which in turn can contain functions, and so on):

x = function() {
  this.y = function() { /* ... */ };
  function z() { /* ... */ }
};

Also, you can have string literals or comments that can contain (sub) strings that either look like functions:

var s = "function notAFunction(){}";
/* 
function alsoNotAFunction(){}
*/

or contain parts of functions your regex would trip over:

function f() {
  var s = "not a closing bracket: } ";
}

So, to answer you question what the regex would be to match functions in JS: it does not exist. You should/could use a proper parser for this.

Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
3

Are you trying to parse JS with regex? If so, DON'T. Regex is a VERY BAD parser see these questions as well.

When should I use a parser?

RegEx match open tags except XHTML self-contained tags

If you're not supposed to use Regular Expressions to parse HTML, then how are HTML parsers written?

Community
  • 1
  • 1
Keng
  • 52,011
  • 32
  • 81
  • 111
1

I wrote a regular expression a while back, to detect functions, so we can check if there is documentation written for every function, not that easy to follow, not an amazing regular expression, but it seems to cover a lot of different types of function declarations:

^(?:[\s]+)?(?:const|let|var|)?(?:[a-z0-9.]+(?:\.prototype)?)?(?:\s)?(?:[a-z0-9-_]+\s?=)?\s?(?:[a-z0-9]+\s+\:\s+)?(?:function\s?)?(?:[a-z0-9_-]+)?\s?\(.*\)\s?(?:.+)?([=>]:)?\{(?:(?:[^}{]+|\{(?:[^}{]+|\{[^}{]*\})*\})*\}(?:\s?\(.*\)\s?\)\s?)?)?(?:\;)?

See the types I've tested here: https://regex101.com/r/WGqfm8/9/

This wont handle every case, but should grab most of them!

Things I purposely didn't add:

// won't find
const filterFn = apples.filter(() => {});
// won't find
const filterFn = apples.filter(function(){});

There's most definitely something I'm not covering, but I tried to cover most of the basics, you'll just need to trim whitespace from the match groups

Shannon Hochkins
  • 11,763
  • 15
  • 62
  • 95
0

Today i faced same challenge, i wanted to find function definations and function usages in a string.

And solved it by using a parser called esprima.

(Note: this is for nodejs , not browser javascript. run dependencies with npm i esprima clipboardy and put the code in index.js and run it with node index.js)

var esprima = require('esprima');
const clipboardy = require('clipboardy');
var program = `
const answer = 42;
function foo(){
    alert("hi")
}
`;

//esprima.tokenize(program)
var entries = []
parsed = esprima.parseScript(program, {}, function (node, meta) {
            entries.push({
                start: meta.start.offset,
                end: meta.end.offset,
                node: node
            });
    })

clipboardy.writeSync(JSON.stringify(entries, null, 2));
console.log('full parsed data copied to clipboard!')

//find function calls
var functionCalls = entries.filter(e => e.node.type == "CallExpression")
console.log('function calls: ' + JSON.stringify(functionCalls.map (a => {return {name: a.node.callee.name, start: a.start, end: a.end}})))

//find alert function calls
var alertFunctionCalls = entries.filter(e => e.node.type == 'CallExpression' && (e.node.callee.name == 'alert'))
console.log('alert function calls: ' + JSON.stringify(alertFunctionCalls.map (a => {return {start: a.start, end: a.end}})))

//find function definations
var functionDeclarations = entries.filter(e => e.node.type == 'FunctionDeclaration')
console.log('function definations: ' + JSON.stringify(functionDeclarations.map (a => {return {name: a.node.id.name, start: a.start, end: a.end}})))

//find foo() function defination
var fooFunctionDeclaration = entries.filter(e => e.node.type == 'FunctionDeclaration' && e.node.id.name == 'foo')
console.log('foo function definations: ' + JSON.stringify(functionDeclarations.map (a => {return {start: a.start, end: a.end}})))

//remove alert function calls
var program2 = program
alertFunctionCalls.sort((a, b) => { return b.end - a.end }).forEach(n => {
    program2 = program2.substring(0, n.start) + program2.substring(n.end);
});
console.log('program after removing alert function calls: ' + program2)

yaya
  • 7,675
  • 1
  • 39
  • 38