Detect Function Argument Names with JavaScript

By  on  

I was recently looking over the promisify-node code to see how the author was able to convert basic functions and objects to a promised-based API.  I quickly realized that they were reading function signatures to look for common callback argument names like callback and cb.  The strategy seemed odd but probably necessary.

I took a few minutes to pick out the JavaScript function which parsed argument names for a function and here it is:

function getArgs(func) {
  // First match everything inside the function argument parens.
  var args = func.toString().match(/function\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;
  });
}

So given the function above and a sample function, here's how it would work:

function myCustomFn(arg1, arg2,arg3) {
  
}

console.log(getArgs(myCustomFn)); // ["arg1", "arg2", "arg3"]

Aren't regular expressions a beautiful thing?  I can't name many uses for such a function but here it is if you're looking to do such a thing!

Recent Features

  • By
    JavaScript Promise API

    While synchronous code is easier to follow and debug, async is generally better for performance and flexibility. Why "hold up the show" when you can trigger numerous requests at once and then handle them when each is ready?  Promises are becoming a big part of the JavaScript world...

  • By
    Page Visibility API

    One event that's always been lacking within the document is a signal for when the user is looking at a given tab, or another tab. When does the user switch off our site to look at something else? When do they come back?

Incredible Demos

  • By
    How to Create a Twitter Card

    One of my favorite social APIs was the Open Graph API adopted by Facebook.  Adding just a few META tags to each page allowed links to my article to be styled and presented the way I wanted them to, giving me a bit of control...

  • By
    FileReader API

    As broadband speed continues to get faster, the web continues to be more media-centric.  Sometimes that can be good (Netflix, other streaming services), sometimes that can be bad (wanting to read a news article but it has an accompanying useless video with it).  And every social service does...

Discussion

  1. MaxArt

    Well, good luck using that regex with ES6 arrow functions :I
    And it can be also tried by comments put among the function’s arguments.

    Man, parsing is _hard_…

  2. Fred

    I guess that’s how Angular injects objects like $scope, $http and so on…

  3. I guess, this will work till the first object destructing in arguments. E.g. in FF:

    function a({test: test}) {}
    a.toString().match(/function\s.*?\(([^)]*)\)/)[1].split(",").map(function(arg) {
        // Ensure no inline comments are parsed and trim the whitespace.
        return arg.replace(/\/\*.*\*\//, "").trim();
      }).filter(function(arg) {
        // Ensure no undefineds are added.
        return arg;
      });
    

    For promisify-node purposes this should be sufficient though.

  4. mihailik

    Chrome and FireFox already have arrow functions in mainstream release builds, so as MaxArt mentioned this fails:

    getArgs((a,b,c) => a+b)
    
    :TypeError: func.toString(...).match(...) is null
    
  5. Is this fast enough for production?

    • Define fast enough.

    • Not that it answers your question, but this approach is the one Angular takes with its dependency injection. Back when I was getting into Angular, I created a simple example of this approach. However these days I’d favour destructuring every time, as Deku does.

  6. Jos de Jong

    Please put a big, big warning in this post that you be careful with relying on argument names: when you minify code, argument names are typically mangled. So if you rely matching arguments by their name like arg1, $scope, or $http, you code will break when minifying it. The Angular folks among us know about the troubles this can give…

  7. stoeffel

    I created a module for this a while ago. https://github.com/stoeffel/retrieve-arguments

  8. Yeah, as Jos de Jong mentioned, don’t do this; it breaks when you minify (Which I hope you’re doing). The way Angular works is that it will look for these but it will first look for an array around the function call to resolve its dependencies and it will also look for an array of arguments keyed $inject on the function to invoke.

    While it is a neat little party trick, ultimately its not all that great.

  9. Allain

    https://www.npmjs.com/package/get-parameter-names has this broken out as an noon package.

  10. sminutoli

    Hi Dave, I’m wondering about es2015 args with default values, destructuring and so on… how do you handle it?

    • This package uses recast in order to create an AST and then the parameter names are gathered from their, this allows it to support pattern matching, default arguments, arrow functions and other ES6 features.

      https://www.npmjs.com/package/es-arguments

  11. Alex de Waal

    The article and many comments interchange the terms “argument” and “parameter”.
    According to https://developer.mozilla.org/en-US/docs/Glossary/Parameter
    – Function parameters are the names listed in the function’s definition.
    – Function arguments are the real values passed to the function.
    – Parameters are initialized to the values of the arguments supplied.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!