Comment obtenir des noms / valeurs de paramètres de fonction dynamiquement?

Existe-t-il un moyen d'obtenir les noms de paramètres de fonction d'une fonction de façon dynamique?

Disons que ma fonction ressemble à ceci:

function doSomething(param1, param2, .... paramN){ // fill an array with the parameter name and value // some other code } 

Maintenant, comment puis-je obtenir une liste des noms de paramètres et leurs valeurs dans un tableau à partir de l'intérieur de la fonction?

    La fonction suivante renverra un tableau des noms de paramètres de toute fonction passée.

     var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var ARGUMENT_NAMES = /([^\s,]+)/g; function getParamNames(func) { var fnStr = func.toString().replace(STRIP_COMMENTS, ''); var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES); if(result === null) result = []; return result; } 

    Exemple d'utilisation:

     getParamNames(getParamNames) // returns ['func'] getParamNames(function (a,b,c,d){}) // returns ['a','b','c','d'] getParamNames(function (a,/*b,c,*/d){}) // returns ['a','d'] getParamNames(function (){}) // returns [] 

    Modifier :

    Avec l'invention de ES6, cette fonction peut être déclenchée par des paramètres par défaut. Voici un hack rapide qui devrait fonctionner dans la plupart des cas:

     var STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg; 

    Je dis la plupart des cas parce qu'il y a des choses qui le dérangent

     function (a=4*(5/3), b) {} // returns ['a'] 

    Edit : Je note également que vikasde veut également les valeurs des paramètres dans un tableau. Ceci est déjà fourni dans une variable locale appelée arguments.

    Extrait de https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments :

    L'objet arguments n'est pas un Array. Il est similaire à un tableau, mais n'a pas de propriétés de matrice, sauf la longueur. Par exemple, il n'a pas la méthode pop. Cependant, il peut être converti en un véritable tableau:

     var args = Array.prototype.slice.call(arguments); 

    Si les génériques Array sont disponibles, on peut utiliser les éléments suivants à la place:

     var args = Array.slice(arguments); 

    Ci-dessous est le code tiré d'AngularJS qui utilise la technique pour son mécanisme d'injection de dépendance.

    Et voici une explication à partir de http://docs.angularjs.org/tutorial/step_05

    L'injecteur de dépendance d'Angular fournit des services à votre contrôleur lorsque le contrôleur est en cours de construction. L'injecteur de dépendance prend également en charge la création de dépendances transitives que le service peut avoir (les services dépendent souvent d'autres services).

    Notez que les noms des arguments sont significatifs, car l'injecteur les utilise pour rechercher les dépendances.

     /** * @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; } 

    La solution moins enclins aux espaces et aux commentaires serait:

     var fn = function(/* whoa) */ hi, you){}; fn.toString() .replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg,'') .match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1] .split(/,/) ["hi", "you"] 

    Voici une solution mise à jour qui tente de traiter de manière compacte tous les cas de bord mentionnés ci-dessus:

     function $args(func) { return (func + '') .replace(/[/][/].*$/mg,'') // strip single-line comments .replace(/\s+/g, '') // strip white space .replace(/[/][*][^/*]*[*][/]/g, '') // strip multi-line comments .split('){', 1)[0].replace(/^[^(]*[(]/, '') // extract the parameters .replace(/=[^,]+/g, '') // strip any ES6 defaults .split(',').filter(Boolean); // split & filter [""] } 

    Sortie de test abrégée (les cas de test complets sont joints ci-dessous):

     'function (a,b,c)...' // returns ["a","b","c"] 'function ()...' // returns [] 'function named(a, b, c) ...' // returns ["a","b","c"] 'function (a /* = 1 */, b /* = true */) ...' // returns ["a","b"] 'function fprintf(handle, fmt /*, ...*/) ...' // returns ["handle","fmt"] 'function( a, b = 1, c )...' // returns ["a","b","c"] 'function (a=4*(5/3), b) ...' // returns ["a","b"] 'function (a, // single-line comment xjunk) ...' // returns ["a","b"] 'function (a /* fooled you...' // returns ["a","b"] 'function (a /* function() yes */, \n /* no, */b)/* omg! */...' // returns ["a","b"] 'function ( A, b \n,c ,d \n ) \n ...' // returns ["A","b","c","d"] 'function (a,b)...' // returns ["a","b"] 'function $args(func) ...' // returns ["func"] 'null...' // returns ["null"] 'function Object() ...' // returns [] 
     function $args(func) { return (func + '') .replace(/[/][/].*$/mg,'') // strip single-line comments .replace(/\s+/g, '') // strip white space .replace(/[/][*][^/*]*[*][/]/g, '') // strip multi-line comments .split('){', 1)[0].replace(/^[^(]*[(]/, '') // extract the parameters .replace(/=[^,]+/g, '') // strip any ES6 defaults .split(',').filter(Boolean); // split & filter [""] } // test cases document.getElementById('console_info').innerHTML = ( [ // formatting -- typical function(a,b,c){}, function(){}, function named(a, b, c) { /* multiline body */ }, // default values -- conventional function(a /* = 1 */, b /* = true */) { a = a||1; b=b||true; }, function fprintf(handle, fmt /*, ...*/) { }, // default values -- ES6 "function( a, b = 1, c ){}", "function (a=4*(5/3), b) {}", // embedded comments -- sardonic function(a, // single-line comment xjunk) {} b //,c,d ) // single-line comment {}, function(a /* fooled you{*/,b){}, function /* are you kidding me? (){} */(a /* function() yes */, /* no, */b)/* omg! */{/*}}*/}, // formatting -- sardonic function ( A, b ,c ,d ) { }, // by reference this.jQuery || function (a,b){return new e.fn.init(a,b,h)}, $args, // inadvertent non-function values null, Object ].map(function(f) { var abbr = (f + '').replace(/\n/g, '\\n').replace(/\s+|[{]+$/g, ' ').split("{", 1)[0] + "..."; return " '" + abbr + "' // returns " + JSON.stringify($args(f)); }).join("\n") + "\n"); // output for copy and paste as a markdown snippet 
     <pre id='console_info'></pre> 

    Beaucoup de réponses ici utilisent les regexes, ça va bien, mais il ne gère pas trop facilement les nouveaux ajouts à la langue (comme les fonctions de la flèche et les classes). Il est également à noter que si vous utilisez une de ces fonctions sur le code minifié, ça va 🔥. Il utilisera n'importe quel nom minifié. Angular contourne cela en vous permettant de passer dans un ensemble ordonné de chaînes qui correspond à l'ordre des arguments lors de leur enregistrement avec le conteneur DI. Alors avec la solution:

     var esprima = require('esprima'); var _ = require('lodash'); const parseFunctionArguments = (func) => { // allows us to access properties that may or may not exist without throwing // TypeError: Cannot set property 'x' of undefined const maybe = (x) => (x || {}); // handle conversion to string and then to JSON AST const functionAsString = func.toString(); const tree = esprima.parse(functionAsString); console.log(JSON.stringify(tree, null, 4)) // We need to figure out where the main params are. Stupid arrow functions 👊 const isArrowExpression = (maybe(_.first(tree.body)).type == 'ExpressionStatement'); const params = isArrowExpression ? maybe(maybe(_.first(tree.body)).expression).params : maybe(_.first(tree.body)).params; // extract out the param names from the JSON AST return _.map(params, 'name'); }; 

    Cela gère le problème d'analyse original et quelques autres types de fonctions (par exemple, les fonctions de flèche). Voici une idée de ce qu'il peut et ne peut pas gérer de la manière suivante:

     // I usually use mocha as the test runner and chai as the assertion library describe('Extracts argument names from function signature. 💪', () => { const test = (func) => { const expectation = ['it', 'parses', 'me']; const result = parseFunctionArguments(toBeParsed); result.should.equal(expectation); } it('Parses a function declaration.', () => { function toBeParsed(it, parses, me){}; test(toBeParsed); }); it('Parses a functional expression.', () => { const toBeParsed = function(it, parses, me){}; test(toBeParsed); }); it('Parses an arrow function', () => { const toBeParsed = (it, parses, me) => {}; test(toBeParsed); }); // ================= cases not currently handled ======================== // It blows up on this type of messing. TBH if you do this it deserves to // fail 😋 On a tech note the params are pulled down in the function similar // to how destructuring is handled by the ast. it('Parses complex default params', () => { function toBeParsed(it=4*(5/3), parses, me) {} test(toBeParsed); }); // This passes back ['_ref'] as the params of the function. The _ref is a // pointer to an VariableDeclarator where the ✨🦄 happens. it('Parses object destructuring param definitions.' () => { function toBeParsed ({it, parses, me}){} test(toBeParsed); }); it('Parses object destructuring param definitions.' () => { function toBeParsed ([it, parses, me]){} test(toBeParsed); }); // Classes while similar from an end result point of view to function // declarations are handled completely differently in the JS AST. it('Parses a class constructor when passed through', () => { class ToBeParsed { constructor(it, parses, me) {} } test(ToBeParsed); }); }); 

    Selon ce que vous voulez utiliser pour ES6 Proxies et la déstructuration peut être votre meilleur pari. Par exemple, si vous vouliez l'utiliser pour l'injection de dépendance (en utilisant les noms des params), vous pouvez le faire comme suit:

     class GuiceJs { constructor() { this.modules = {} } resolve(name) { return this.getInjector()(this.modules[name]); } addModule(name, module) { this.modules[name] = module; } getInjector() { var container = this; return (klass) => { console.log(klass); var paramParser = new Proxy({}, { // The `get` handler is invoked whenever a get-call for // `injector.*` is made. We make a call to an external service // to actually hand back in the configured service. The proxy // allows us to bypass parsing the function params using // taditional regex or even the newer parser. get: (target, name) => container.resolve(name), // You shouldn't be able to set values on the injector. set: (target, name, value) => { throw new Error(`Don't try to set ${name}! 😑`); } }) return new klass(paramParser); } } } 

    Ce n'est pas le résolveur le plus avancé, mais il donne une idée de la façon dont vous pouvez utiliser un Proxy pour le gérer si vous souhaitez utiliser l'analyseur args pour une DI simple. Il y a cependant une légère lacune dans cette approche. Nous devons utiliser les affectations de déstructuration au lieu des paramètres normaux. Lorsque nous passons dans le proxy de l'injecteur, la déstructuration est la même que l'appel du getter sur l'objet.

     class App { constructor({tweeter, timeline}) { this.tweeter = tweeter; this.timeline = timeline; } } class HttpClient {} class TwitterApi { constructor({client}) { this.client = client; } } class Timeline { constructor({api}) { this.api = api; } } class Tweeter { constructor({api}) { this.api = api; } } // Ok so now for the business end of the injector! const di = new GuiceJs(); di.addModule('client', HttpClient); di.addModule('api', TwitterApi); di.addModule('tweeter', Tweeter); di.addModule('timeline', Timeline); di.addModule('app', App); var app = di.resolve('app'); console.log(JSON.stringify(app, null, 4)); 

    Cela produit ce qui suit:

     { "tweeter": { "api": { "client": {} } }, "timeline": { "api": { "client": {} } } } 

    Il a filé toute l'application. Le meilleur moyen est que l'application est facile à tester (vous pouvez simplement faire une instanciation dans chaque classe et passer à la molette / stubs / etc). De plus, si vous devez échanger des implémentations, vous pouvez le faire en un seul endroit. Tout cela est possible en raison des objets JS Proxy.

    Remarque: Il y a beaucoup de travail qui devrait être fait avant qu'il ne soit prêt à l'emploi de la production, mais il donne une idée de ce qu'il ressemblerait.

    C'est un peu tard dans la réponse, mais cela peut aider d'autres qui pensent à la même chose. 👍

    Je sais que c'est une vieille question, mais les débutants ont été copypasting cela comme si c'était une bonne pratique dans n'importe quel code. La plupart du temps, avoir à analyser la représentation de chaîne d'une fonction pour utiliser ses noms de paramètres, cache un défaut dans la logique du code.

    Les paramètres d'une fonction sont effectivement stockés dans un objet en forme de tableau appelé arguments , où le premier argument est les arguments[0] , le second est les arguments[1] et ainsi de suite. L'écriture de noms de paramètres dans les parenthèses peut être considérée comme une syntaxe abrégée. Ce:

     function doSomething(foo, bar) { console.log("does something"); } 

    …est le même que:

     function doSomething() { var foo = arguments[0]; var bar = arguments[1]; console.log("does something"); } 

    Les variables elles-mêmes sont stockées dans la portée de la fonction, et non comme propriétés d'un objet. Il n'y a aucun moyen de récupérer le nom du paramètre à travers le code car il s'agit simplement d'un symbole représentant la variable en langue humaine.

    J'ai toujours considéré la représentation de la chaîne d'une fonction comme un outil à des fins de débogage, en particulier à cause de cet arguments un objet en forme de tableau. Vous n'êtes pas tenu de donner des noms aux arguments en premier lieu. Si vous essayez d'analyser une fonction stringée, il ne vous indique pas réellement des paramètres supplémentaires sans nom que cela pourrait prendre.

    Voici une situation encore pire et plus commune. Si une fonction a plus de 3 ou 4 arguments, il est logique de passer un objet à la place, ce qui est plus facile à utiliser.

     function saySomething(obj) { if(obj.message) console.log((obj.sender || "Anon") + ": " + obj.message); } saySomething({sender: "user123", message: "Hello world"}); 

    Dans ce cas, la fonction elle-même sera en mesure de lire l'objet qu'il reçoit et de rechercher ses propriétés et d'obtenir à la fois ses noms et ses valeurs, mais en essayant d'analyser la représentation de la chaîne, la fonction ne vous donnera que "obj" pour les paramètres, Ce qui n'est pas du tout utile.

     (function(a,b,c){}).toString().replace(/.*\(|\).*/ig,"").split(',') 

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

    J'ai essayé de le faire auparavant, mais je n'ai jamais trouvé une façon pratique de le faire. J'ai fini par passer dans un objet à la place, puis je l'écoutais.

     //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 }); 

    Je ne sais pas si cette solution correspond à votre problème, mais elle vous permet de redéfinir toute fonction que vous voulez, sans avoir à changer le code qui l'utilise. Les appels existants utiliseront des paramètres privilégiés, alors que l'implémentation de la fonction peut utiliser "Params nommés" (un seul paramètre de hash).

    Je pensais que vous modifiez les définitions des fonctions existantes, de façon à ne pas avoir une fonction d'usine qui fait exactement ce que vous voulez:

     <!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> 

    J'espère que cela aide.

    Vous pouvez également utiliser l'analyseur "esprima" pour éviter de nombreux problèmes avec des commentaires, des espaces et d'autres éléments dans la liste des paramètres.

     function getParameters(yourFunction) { var i, // safetyValve is necessary, because sole "function () {...}" // is not a valid syntax parsed = esprima.parse("safetyValve = " + yourFunction.toString()), params = parsed.body[0].expression.right.params, ret = []; for (i = 0; i < params.length; i += 1) { ret.push(params[i].name); } return ret; } 

    Cela fonctionne même avec un code comme celui-ci:

     getParameters(function (hello /*, foo ),* /bar* { */,world) {}); // ["hello", "world"] 

    Et je voudrais faire de la publicité sur mon lib qui fait cela sous Node.js également: https://github.com/cruks/cruks-lib-function

    Je ne sais pas comment obtenir une liste des paramètres, mais vous pouvez le faire pour obtenir combien il attend.

     alert(doSomething.length); 

    Comment je le fais généralement:

     function name(arg1, arg2){ var args = arguments; // array: [arg1, arg2] var objecArgOne = args[0].one; } name({one: "1", two: "2"}, "string"); 

    Vous pouvez même renvoyer les args par le nom des fonctions comme:

     name.arguments; 

    J'espère que cela t'aides!

     //See this: // global var, naming bB var bB = 5; // Dependency Injection cokntroller var a = function(str, fn) { //stringify function body var fnStr = fn.toString(); // Key: get form args to string var args = fnStr.match(/function\s*\((.*?)\)/); // console.log(args); // if the form arg is 'bB', then exec it, otherwise, do nothing for (var i = 0; i < args.length; i++) { if(args[i] == 'bB') { fn(bB); } } } // will do nothing a('sdfdfdfs,', function(some){ alert(some) }); // will alert 5 a('sdfdsdsfdfsdfdsf,', function(bB){ alert(bB) }); // see, this shows you how to get function args in string 

    En prenant la réponse à partir de @ jack-allan, j'ai légèrement modifié la fonction pour autoriser les propriétés par défaut ES6 telles que:

     function( a, b = 1, c ){}; 

    Pour encore retourner [ 'a', 'b' ]

     /** * Get the keys of the paramaters of a function. * * @param {function} method Function to get parameter keys for * @return {array} */ var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var ARGUMENT_NAMES = /(?:^|,)\s*([^\s,=]+)/g; function getFunctionParameters ( func ) { var fnStr = func.toString().replace(STRIP_COMMENTS, ''); var argsList = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')); var result = argsList.match( ARGUMENT_NAMES ); if(result === null) { return []; } else { var stripped = []; for ( var i = 0; i < result.length; i++ ) { stripped.push( result[i].replace(/[\s,]/g, '') ); } return stripped; } } 

    La réponse à cela nécessite 3 étapes:

    1. Pour obtenir les valeurs des paramètres réels passés à la fonction (appelons-le argValues ). Ceci est simple car il sera disponible en tant arguments dans la fonction.
    2. Pour obtenir les noms des paramètres à partir de la signature de la fonction (appelez-le argNames ). Ce n'est pas aussi simple et nécessite l'analyse de la fonction. Au lieu de faire le regex complexe vous-même et de vous soucier des cas de bord (paramètres par défaut, commentaires, …), vous pouvez utiliser une bibliothèque comme Babylon qui analysera la fonction dans un arbre de syntaxe abstraite dont vous pouvez obtenir les noms des paramètres.
    3. La dernière étape consiste à joindre les 2 tableaux ensemble en 1 tableau qui a le nom et la valeur de tous les paramètres.

    Le code sera ainsi

     const babylon = require("babylon") function doSomething(a, b, c) { // get the values of passed argumenst const argValues = arguments // get the names of the arguments by parsing the function const ast = babylon.parse(doSomething.toString()) const argNames = ast.program.body[0].params.map(node => node.name) // join the 2 arrays, by looping over the longest of 2 arrays const maxLen = Math.max(argNames.length, argValues.length) const args = [] for (i = 0; i < maxLen; i++) { args.push({name: argNames[i], value: argValues[i]}) } console.log(args) // implement the actual function here } doSomething(1, 2, 3, 4) 

    Et l'objet enregistré sera

     [ { "name": "a", "value": 1 }, { "name": "c", "value": 3 }, { "value": 4 } ] 

    Et voici un exemple de travail https://tonicdev.com/5763eb77a945f41300f62a79/5763eb77a945f41300f62a7a

    J'ai lu la plupart des réponses ici, et j'aimerais ajouter mon one-liner.

     new RegExp(Function.name+'\\s*\\((.*?)\\)').exec(Function.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '') 

    ou

     function getParameters(function) { return new RegExp(function.name+'\\s*\\((.*?)\\)').exec(function.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, ''); } 

    Disons que vous avez une fonction

     function foo(abc, def, ghi, jkl) { //code } 

    Le code ci-dessous renverra "abc,def,ghi,jkl"

    Ce code fonctionnera également avec la mise en place d'une fonction que Camilo Martin a donnée:

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

    Aussi avec le commentaire de Bubersson sur la réponse de Jack Allan :

     function(a /* fooled you)*/,b){} 

    __

    Explication

    new RegExp(Function.name+'\\s*\\((.*?)\\)')

    Cela crée un Exponent régulier avec le new RegExp(Function.name+'\\s*\\((.*?)\\)') . Je dois utiliser le new RegExp parce que je suis en train d'injecter une variable (Nom Function.name , le nom de la fonction ciblée) dans RegExp.

    Exemple Si le nom de la fonction est "foo" ( function foo() ), le RegExp sera / /foo\s*\((.*?)\)/ . /foo\s*\((.*?)\)/ ?) /foo\s*\((.*?)\)/ .

    Function.toString().replace(/\n/g, '')

    Ensuite, il convertit la fonction entière en une chaîne et supprime toutes les nouvelles lignes. L'élimination des nouvelles lignes contribue à la configuration de la fonction Camilo Martin .

    .exec(...)[1]

    Il s'agit de la fonction RegExp.prototype.exec . Il correspond essentiellement à l'Exponent régulier ( new RegExp() ) dans String ( Function.toString() ). Ensuite, [1] renverra le premier groupe de capture trouvé dans l'Exponent ordinaire ( (.*?) ).

    .replace(/\/\*.*?\*\//g, '').replace(/ /g, '')

    Cela supprime tous les commentaires à l'intérieur /* et */ , et supprime tous les espaces.


    Si vous souhaitez créer tous les paramètres dans un tableau au lieu d'une chaîne séparée par des virgules, à la fin, ajoutez simplement .split(',') .

     function getArgs(args) { var argsObj = {}; var argList = /\(([^)]*)/.exec(args.callee)[1]; var argCnt = 0; var tokens; while (tokens = /\s*([^,]+)/g.exec(argList)) { argsObj[tokens[1]] = args[argCnt++]; } return argsObj; } 

    Wow tant de réponses déjà … Je suis très sûr que cela soit enterré. Même si j'ai pensé que cela pourrait être utile pour certains.

    Je n'étais pas entièrement satisfait des réponses choisies, comme dans ES6, cela ne fonctionne pas bien avec les valeurs par défaut. Et il ne fournit pas non plus les informations de valeur par défaut. Je voulais aussi une fonction légère qui ne dépend pas d'une lib. Externe.

    Cette fonction est très utile à des fins de débogage, par exemple: journalisation appelée fonction avec ses paramètres, valeurs par défaut des paramètres et arguments.

    J'ai passé un peu de temps hier, en crachant le bon RegExp pour résoudre ce problème et c'est ce que j'ai proposé. Cela fonctionne très bien et je suis très satisfait du résultat:

     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\'' ] ] 

    You can access the argument values passed to a function using the "arguments" property.

      function doSomething() { var args = doSomething.arguments; var numArgs = args.length; for(var i = 0 ; i < numArgs ; i++) { console.log("arg " + (i+1) + " = " + args[i]); //console.log works with firefox + firebug // you can use an alert to check in other browsers } } doSomething(1, '2', {A:2}, [1,2,3]); 

    Here's one way:

     // Utility function to extract arg name-value pairs function getArgs(args) { var argsObj = {}; var argList = /\(([^)]*)/.exec(args.callee)[1]; var argCnt = 0; var tokens; while (tokens = /\s*([^,]+)/g.exec(argList)) { argsObj[tokens[1]] = args[argCnt++]; } return argsObj; } // Test subject function add(number1, number2) { var args = getArgs(arguments); alert(args.toSource()); // ({number1:3,number2:4}) } // Invoke test subject add(3, 4); 

    Note: This only works on browsers that support arguments.callee .

    It's pretty easy.

    At the first there is a deprecated arguments.callee — a reference to called function. At the second if you have a reference to your function you can easily get their textual representation. At the third if you calling your function as constructor you can also have a link via yourObject.constructor. NB: The first solution deprecated so if you can't to not use it you must also think about your app architecture. If you don't need exact variable names just use inside a function internal variable arguments without any magic.

    https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee

    All of them going to call toString and replace with re so we can create a helper:

     // getting names of declared parameters var getFunctionParams = function (func) { return String(func).replace(/[^\(]+\(([^\)]*)\).*/m, '$1'); } 

    Quelques exemples:

     // Solution 1. deprecated! don't use it! var myPrivateFunction = function SomeFuncName (foo, bar, buz) { console.log(getFunctionParams(arguments.callee)); }; myPrivateFunction (1, 2); // Solution 2. var myFunction = function someFunc (foo, bar, buz) { // some code }; var params = getFunctionParams(myFunction); console.log(params); // Solution 3. var cls = function SuperKewlClass (foo, bar, buz) { // some code }; var inst = new cls(); var params = getFunctionParams(inst.constructor); console.log(params); 

    Enjoy with JS!

    UPD: Jack Allan was provided a little bit better solution actually. GJ Jack!

    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.

    Ok so an old question with plenty of adequate answers. here is my offering that does not use regex, except for the menial task of stripping whitespace . (I should note that the "strips_comments" function actually spaces them out, rather than physically remove them. that's because i use it elsewhere and for various reasons need the locations of the original non comment tokens to stay intact)

    It's a fairly lengthy block of code as this pasting includes a mini test framework.

      function do_tests(func) { if (typeof func !== 'function') return true; switch (typeof func.tests) { case 'undefined' : return true; case 'object' : for (var k in func.tests) { var test = func.tests[k]; if (typeof test==='function') { var result = test(func); if (result===false) { console.log(test.name,'for',func.name,'failed'); return false; } } } return true; case 'function' : return func.tests(func); } return true; } function strip_comments(src) { var spaces=(s)=>{ switch (s) { case 0 : return ''; case 1 : return ' '; case 2 : return ' '; default : return Array(s+1).join(' '); } }; var c1 = src.indexOf ('/*'), c2 = src.indexOf ('//'), eol; var out = ""; var killc2 = () => { out += src.substr(0,c2); eol = src.indexOf('\n',c2); if (eol>=0) { src = spaces(eol-c2)+'\n'+src.substr(eol+1); } else { src = spaces(src.length-c2); return true; } return false; }; while ((c1>=0) || (c2>=0)) { if (c1>=0) { // c1 is a hit if ( (c1<c2) || (c2<0) ) { // and it beats c2 out += src.substr(0,c1); eol = src.indexOf('*/',c1+2); if (eol>=0) { src = spaces((eol-c1)+2)+src.substr(eol+2); } else { src = spaces(src.length-c1); break; } } else { if (c2 >=0) { // c2 is a hit and it beats c1 if (killc2()) break; } } } else { if (c2>=0) { // c2 is a hit, c1 is a miss. if (killc2()) break; } else { // both c1 & c2 are a miss break; } } c1 = src.indexOf ('/*'); c2 = src.indexOf ('//'); } return out + src; } function function_args(fn) { var src = strip_comments(fn.toString()); var names=src.split(')')[0].replace(/\s/g,'').split('(')[1].split(','); return names; } function_args.tests = [ function test1 () { function/*al programmers will sometimes*/strip_comments_tester/* because some comments are annoying*/( /*see this---(((*/ src//)) it's an annoying comment does not help anyone understand if the ,code,//really does /**/sucks ,much /*?*/)/*who would put "comment\" about a function like (this) { comment } here?*/{ } var data = function_args(strip_comments_tester); return ( (data.length==4) && (data[0]=='src') && (data[1]=='code') && (data[2]=='sucks') && (data[3]=='much') ); } ]; do_tests(function_args); 

    By using the arguments array in JS:

     function doSomething(){ for (var i = 0, j = arguments.length; i < j; i++){ alert(arguments[i]); } } doSomething('a','b','c') 

    function parameter string value image dynamically from JSON . Since item.product_image2 is a URL string, you need to put it in quotes when you call changeImage inside parameter.

    My Function Onclick

     items+='<img src='+item.product_image1+' id="saleDetailDivGetImg">'; items+="<img src="+item.product_image2+" onclick='changeImage(\""+item.product_image2+"\");'>"; 

    My Function

     <script type="text/javascript"> function changeImage(img) { document.getElementById("saleDetailDivGetImg").src=img; alert(img); } </script> 

    I'll give you a short example below:

     function test(arg1,arg2){ var funcStr = test.toString() var leftIndex = funcStr.indexOf('('); var rightIndex = funcStr.indexOf(')'); var paramStr = funcStr.substr(leftIndex+1,rightIndex-leftIndex-1); var params = paramStr.split(','); for(param of params){ console.log(param); // arg1,arg2 } } test();