How to get function parameter names/values dynamically?

前端 未结 30 2441
说谎
说谎 2020-11-22 00:13

Is there a way to get the function parameter names of a function dynamically?

Let’s say my function looks like this:

function doSomething(param1, par         


        
相关标签:
30条回答
  • 2020-11-22 00:22
    (function(a,b,c){}).toString().replace(/.*\(|\).*/ig,"").split(',')
    

    => [ "a", "b", "c" ]

    0 讨论(0)
  • 2020-11-22 00:23

    Below is the code taken from AngularJS which uses the technique for its dependency injection mechanism.

    And here is an explanation of it taken from http://docs.angularjs.org/tutorial/step_05

    Angular's dependency injector provides services to your controller when the controller is being constructed. The dependency injector also takes care of creating any transitive dependencies the service may have (services often depend upon other services).

    Note that the names of arguments are significant, because the injector uses these to look up the dependencies.

    /**
     * @ngdoc overview
     * @name AUTO
     * @description
     *
     * Implicit module which gets automatically added to each {@link AUTO.$injector $injector}.
     */
    
    var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
    var FN_ARG_SPLIT = /,/;
    var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
    var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
    function annotate(fn) {
      var $inject,
          fnText,
          argDecl,
          last;
    
      if (typeof fn == 'function') {
        if (!($inject = fn.$inject)) {
          $inject = [];
          fnText = fn.toString().replace(STRIP_COMMENTS, '');
          argDecl = fnText.match(FN_ARGS);
          forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
            arg.replace(FN_ARG, function(all, underscore, name){
              $inject.push(name);
            });
          });
          fn.$inject = $inject;
        }
      } else if (isArray(fn)) {
        last = fn.length - 1;
        assertArgFn(fn[last], 'fn')
        $inject = fn.slice(0, last);
      } else {
        assertArgFn(fn, 'fn', true);
      }
      return $inject;
    }
    
    0 讨论(0)
  • 2020-11-22 00:23

    I've tried doing this before, but never found a praticial way to get it done. I ended up passing in an object instead and then looping through it.

    //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
    });
    
    0 讨论(0)
  • 2020-11-22 00:26

    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

    0 讨论(0)
  • 2020-11-22 00:27

    I don't know if this solution suits your problem, but it lets you redefine whatever function you want, without having to change code that uses it. Existing calls will use positioned params, while the function implementation may use "named params" (a single hash param).

    I thought that you will anyway modify existing function definitions so, why not having a factory function that makes just what you want:

    <!DOCTYPE html>
    
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    <script type="text/javascript">
    var withNamedParams = function(params, lambda) {
        return function() {
            var named = {};
            var max   = arguments.length;
    
            for (var i=0; i<max; i++) {
                named[params[i]] = arguments[i];
            }
    
            return lambda(named);
        };
    };
    
    var foo = withNamedParams(["a", "b", "c"], function(params) {
        for (var param in params) {
            alert(param + ": " + params[param]);
        }
    });
    
    foo(1, 2, 3);
    </script>
    </head>
    <body>
    
    </body>
    </html>
    

    Hope it helps.

    0 讨论(0)
  • 2020-11-22 00:27

    Whatever the solution, it must not break on wierd functions, whose toString() looks just as wierd:

    function  (  A,  b
    ,c      ,d
    ){}
    

    screenshot from console

    Also, why use complex regular expressions? This can be done like:

    function getArguments(f) {
        return f.toString().split(')',1)[0].replace(/\s/g,'').substr(9).split(',');
    }
    

    This works everywhere with every function, and the only regex is whitespace removal that doesn't even process the whole string due to the .split trick.

    0 讨论(0)
提交回复
热议问题