How to get function parameter names/values dynamically?

前端 未结 30 2597
说谎
说谎 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:41

    Wow so many answers already.. Im pretty sure this gets buried. Even so I figured this might be useful for some.

    I wasn't fully satisfied with the chosen answers as in ES6 it doesn't work well with default values. And it also does not provide the default value information. I also wanted a lightweight function that does not depend on an external lib.

    This function is very useful for debugging purposes, for example: logging called function with its params, default param values and arguments.

    I spent some time on this yesterday, cracking the right RegExp to solve this issue and this is what I came up with. It works very well and I'm very pleased with the outcome:

    const REGEX_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
    const REGEX_FUNCTION_PARAMS = /(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m
    const REGEX_PARAMETERS_VALUES = /\s*(\w+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm
    
    /**
     * Retrieve a function's parameter names and default values
     * Notes:
     *  - parameters with default values will not show up in transpiler code (Babel) because the parameter is removed from the function.
     *  - does NOT support inline arrow functions as default values
     *      to clarify: ( name = "string", add = defaultAddFunction )   - is ok
     *                  ( name = "string", add = ( a )=> a + 1 )        - is NOT ok
     *  - does NOT support default string value that are appended with a non-standard ( word characters or $ ) variable name
     *      to clarify: ( name = "string" + b )         - is ok
     *                  ( name = "string" + $b )        - is ok
     *                  ( name = "string" + b + "!" )   - is ok
     *                  ( name = "string" + λ )         - is NOT ok
     * @param {function} func
     * @returns {Array} - An array of the given function's parameter [key, default value] pairs.
     */
    function getParams(func) {
    
      let functionAsString = func.toString()
      let params = []
      let match
      functionAsString = functionAsString.replace(REGEX_COMMENTS, '')
      functionAsString = functionAsString.match(REGEX_FUNCTION_PARAMS)[1]
      if (functionAsString.charAt(0) === '(') functionAsString = functionAsString.slice(1, -1)
      while (match = REGEX_PARAMETERS_VALUES.exec(functionAsString)) params.push([match[1], match[2]])
      return params
    
    }
    
    
    
    // Lets run some tests!
    
    var defaultName = 'some name'
    
    function test1(param1, param2, param3) { return (param1) => param1 + param2 + param3 }
    function test2(param1, param2 = 4 * (5 / 3), param3) {}
    function test3(param1, param2 = "/root/" + defaultName + ".jpeg", param3) {}
    function test4(param1, param2 = (a) => a + 1) {}
    
    console.log(getParams(test1)) 
    console.log(getParams(test2))
    console.log(getParams(test3))
    console.log(getParams(test4))
    
    // [ [ 'param1', undefined ], [ 'param2', undefined ], [ 'param3', undefined ] ]
    // [ [ 'param1', undefined ], [ 'param2', '4 * (5 / 3)' ], [ 'param3', undefined ] ]
    // [ [ 'param1', undefined ], [ 'param2', '"/root/" + defaultName + ".jpeg"' ], [ 'param3', undefined ] ]
    // [ [ 'param1', undefined ], [ 'param2', '( a' ] ]
    // --> This last one fails because of the inlined arrow function!
    
    
    var arrowTest1 = (a = 1) => a + 4
    var arrowTest2 = a => b => a + b
    var arrowTest3 = (param1 = "/" + defaultName) => { return param1 + '...' }
    var arrowTest4 = (param1 = "/" + defaultName, param2 = 4, param3 = null) => { () => param3 ? param3 : param2 }
    
    console.log(getParams(arrowTest1))
    console.log(getParams(arrowTest2))
    console.log(getParams(arrowTest3))
    console.log(getParams(arrowTest4))
    
    // [ [ 'a', '1' ] ]
    // [ [ 'a', undefined ] ]
    // [ [ 'param1', '"/" + defaultName' ] ]
    // [ [ 'param1', '"/" + defaultName' ], [ 'param2', '4' ], [ 'param3', 'null' ] ]
    
    
    console.log(getParams((param1) => param1 + 1))
    console.log(getParams((param1 = 'default') => { return param1 + '.jpeg' }))
    
    // [ [ 'param1', undefined ] ]
    // [ [ 'param1', '\'default\'' ] ]

    As you can tell some of the parameter names disappear because the Babel transpiler removes them from the function. If you would run this in the latest NodeJS it works as expected (The commented results are from NodeJS).

    Another note, as stated in the comment is that is does not work with inlined arrow functions as a default value. This simply makes it far to complex to extract the values using a RegExp.

    Please let me know if this was useful for you! Would love to hear some feedback!

提交回复
热议问题