Existe uma maneira de obter os nomes de parâmetros de função de uma função dinamicamente?
Digamos que minha função fique assim:
function doSomething(param1, param2, .... paramN){
// fill an array with the parameter name and value
// some other code
}
Agora, como eu obteria uma lista dos nomes de parâmetros e seus valores em uma matriz de dentro da função?
javascript
reflection
function-parameter
Vikasde
fonte
fonte
function doSomething(...args) { /*use args*/}
Respostas:
A função a seguir retornará uma matriz dos nomes de parâmetros de qualquer função passada.
Exemplo de uso:
Editar :
Com o inventário do ES6, essa função pode ser acionada por parâmetros padrão. Aqui está um hack rápido que deve funcionar na maioria dos casos:
Digo a maioria dos casos, porque há algumas coisas que vão enganar
Edit : Também notei que o vikasde também quer os valores dos parâmetros em uma matriz. Isso já é fornecido em uma variável local chamada argumentos.
trecho de https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments :
O objeto de argumentos não é uma matriz. É semelhante a uma matriz, mas não possui nenhuma propriedade da matriz, exceto o comprimento. Por exemplo, ele não possui o método pop. No entanto, pode ser convertido em uma matriz real:
Se os genéricos de matriz estiverem disponíveis, você poderá usar o seguinte:
fonte
var fn = function(a /* fooled you)*/,b){};
irá resultar em["a", "/*", "fooled", "you"]
Abaixo está o código retirado do AngularJS, que usa a técnica para seu mecanismo de injeção de dependência.
E aqui está uma explicação tirada em http://docs.angularjs.org/tutorial/step_05
fonte
annotate = angular.injector.$$annotate
Aqui está uma solução atualizada que tenta solucionar todos os casos extremos mencionados acima de maneira compacta:
Saída de teste abreviada (casos de teste completos estão anexados abaixo):
Mostrar snippet de código
fonte
return (func+'') .replace(/[/][/].*$/mg,'') // strip single-line comments (line-ending sensitive, so goes first) .replace(/\s+/g,'') // remove whitespace
func + ''
porFunction.toString.call(func)
para se defender contra o caso quando a função tiver uma implementação .toString () personalizada..split(/\)[\{=]/, 1)[0]
({ a, b, c })
) em todos os parâmetros dentro da desestruturação. para manter intactos os objetos destruídos, altere o último.split
para: #.split(/,(?![^{]*})/g)
A solução menos propensa a erros de espaços e comentários seria:
fonte
Muitas das respostas aqui usam expressões regulares, isso é bom, mas ele não lida com novas adições muito bem ao idioma (como funções e classes de seta). Também é importante notar que, se você usar qualquer uma dessas funções no código minificado, isso irá 🔥. Ele usará qualquer que seja o nome minificado. Angular contorna isso, permitindo que você passe uma matriz ordenada de seqüências de caracteres que corresponde à ordem dos argumentos ao registrá-los no contêiner DI. Então, com a solução:
Isso lida com o problema de análise original e mais alguns tipos de funções (por exemplo, funções de seta). Aqui está uma idéia do que ele pode e não pode manipular como está:
Dependendo do que você deseja usá-lo nos ES6 Proxies, a desestruturação pode ser sua melhor aposta. Por exemplo, se você quiser usá-lo para injeção de dependência (usando os nomes dos parâmetros), poderá fazer o seguinte:
Não é o resolvedor mais avançado disponível no mercado, mas fornece uma idéia de como você pode usar um Proxy para lidar com isso, se quiser usar o analisador de args para DI simples. Há, no entanto, uma pequena ressalva nessa abordagem. Precisamos usar atribuições de desestruturação em vez de parâmetros normais. Quando passamos no proxy do injetor, a desestruturação é a mesma que chamar o getter no objeto.
Isso gera o seguinte:
Está ligado a aplicação inteira. O melhor é que o aplicativo é fácil de testar (você pode instanciar cada classe e passar em zombarias / stubs / etc). Além disso, se você precisar trocar as implementações, poderá fazer isso a partir de um único local. Tudo isso é possível por causa dos objetos JS Proxy.
Nota: Há muito trabalho que precisaria ser feito para isso antes de estar pronto para uso em produção, mas fornece uma idéia de como seria.
É um pouco tarde na resposta, mas pode ajudar outras pessoas que estão pensando na mesma coisa. 👍
fonte
Eu sei que essa é uma pergunta antiga, mas os iniciantes estão copiando isso como se isso fosse uma boa prática em qualquer código. Na maioria das vezes, ter que analisar a representação de string de uma função para usar seus nomes de parâmetro apenas oculta uma falha na lógica do código.
Os parâmetros de uma função são realmente armazenados em um objeto semelhante a um array chamado
arguments
, onde o primeiro argumento éarguments[0]
, o segundo éarguments[1]
e assim por diante. Escrever nomes de parâmetros entre parênteses pode ser visto como uma sintaxe abreviada. Este:...é o mesmo que:
As próprias variáveis são armazenadas no escopo da função, não como propriedades em um objeto. Não há como recuperar o nome do parâmetro através do código, pois é apenas um símbolo que representa a variável na linguagem humana.
Eu sempre considerei a representação de string de uma função como uma ferramenta para fins de depuração, especialmente por causa desse
arguments
objeto semelhante a uma matriz. Você não precisa dar nomes aos argumentos em primeiro lugar. Se você tentar analisar uma função stringified, ela não informa sobre parâmetros extras sem nome que podem ser necessários.Aqui está uma situação ainda pior e mais comum. Se uma função tiver mais de 3 ou 4 argumentos, pode ser lógico passar um objeto para ela, o que é mais fácil de trabalhar.
Nesse caso, a própria função poderá ler o objeto que recebe, procurar suas propriedades e obter seus nomes e valores, mas tentar analisar a representação de string da função forneceria apenas "obj" para parâmetros, o que não é útil.
fonte
Function.prototype.toString = function () { return 'function () { [native code] }'; };
Proxy
( armadilha de construtor e aplicar armadilha ) eReflection
para lidar com DI para alguns dos meus módulos. É razoavelmente sólido.Como o JavaScript é uma linguagem de script, acho que sua introspecção deve oferecer suporte à obtenção de nomes de parâmetros de funções. Insistir com essa funcionalidade é uma violação dos primeiros princípios, por isso decidi explorar mais o assunto.
Isso me levou a essa pergunta, mas sem soluções internas. O que me levou a essa resposta, que explica que
arguments
só é preterida fora da função, portanto não podemos mais usarmyFunction.arguments
ou obtemos:Hora de arregaçar as mangas e começar a trabalhar:
⭐ A recuperação de parâmetros de função requer um analisador, pois expressões complexas como
4*(5/3)
podem ser usadas como valores padrão. Portanto, a resposta de Gaafar ou a de James Drew são as melhores abordagens até agora.Tentei os analisadores de babylon e esprima, mas infelizmente eles não conseguem analisar funções anônimas independentes, como apontado na resposta de Mateusz Charytoniuk . Eu descobri outra solução alternativa, cercando o código entre parênteses, para não alterar a lógica:
As novas linhas evitam problemas com
//
(comentários de linha única).⭐ Se um analisador não estiver disponível, a próxima melhor opção é usar uma técnica comprovada, como as expressões regulares do injetor de dependência do Angular.js. Combinei uma versão funcional da resposta do Lambder com a resposta do humbletim e adicionei um
ARROW
booleano opcional para controlar se as funções de seta gorda do ES6 são permitidas pelas expressões regulares.Aqui estão duas soluções que eu montei. Observe que eles não têm lógica para detectar se uma função tem sintaxe válida; eles apenas extraem os argumentos. Isso geralmente é bom, já que geralmente transmitimos funções analisadas para
getArguments()
que sua sintaxe já seja válida.Tentarei selecionar essas soluções da melhor maneira possível, mas sem o esforço dos mantenedores do JavaScript, isso continuará sendo um problema em aberto.
Versão do Node.js. (não pode ser executada até que o StackOverflow ofereça suporte ao Node.js.):
Exemplo de trabalho completo:
https://repl.it/repls/SandybrownPhonyAngles
Versão do navegador (observe que ele para no primeiro valor padrão complexo):
Exemplo de trabalho completo:
https://repl.it/repls/StupendousShowyOffices
fonte
Eu li a maioria das respostas aqui e gostaria de adicionar minha linha única.
ou
ou para uma função de uma linha no ECMA6
__
Digamos que você tenha uma função
O código abaixo retornará
"abc,def,ghi,jkl"
Esse código também funcionará com a configuração de uma função que Camilo Martin deu:
Também com o comentário de Bubersson na resposta de Jack Allan :
__
Explicação
new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')
Isso cria uma expressão regular com o
new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')
. Eu tenho que usarnew RegExp
porque estou injetando uma variável (Function.name
, o nome da função que está sendo direcionada) no RegExp.Exemplo Se o nome da função for "foo" (
function foo()
), o RegExp será/foo\s*\((.*?)\)/
.Function.toString().replace(/\n/g, '')
Em seguida, converte toda a função em uma string e remove todas as novas linhas. A remoção de novas linhas ajuda na configuração de funções que Camilo Martin deu.
.exec(...)[1]
Essa é a
RegExp.prototype.exec
função. Basicamente, corresponde ao Expoente Regular (new RegExp()
) na String (Function.toString()
). Em seguida[1]
, retornará o primeiro grupo de captura encontrado no expoente regular ((.*?)
)..replace(/\/\*.*?\*\//g, '').replace(/ /g, '')
Isso removerá todos os comentários internos
/*
e*/
, e removerá todos os espaços.Agora, agora também é possível ler e entender
=>
funções arrow ( ), comof = (a, b) => void 0;
, nas quaisFunction.toString()
retornariam em(a, b) => void 0
vez das funções normaisfunction f(a, b) { return void 0; }
. A expressão regular original teria gerado um erro em sua confusão, mas agora é contabilizada.A mudança foi de
new RegExp(Function.name+'\\s*\\((.*?)\\)')
(/Function\s*\((.*?)\)/
) paranew RegExp('(?:'+Function.name+'\\s*|^)\\((.*?)\\)')
(/(?:Function\s*|^)\((.*?)\)/
)Se você deseja transformar todos os parâmetros em uma matriz, em vez de uma string separada por vírgulas, no final, basta adicionar
.split(',')
.fonte
f = (a, b) => void 0
; emgetParameters(f)
ReceboTypeError: Cannot read property '1' of null
getParameters(a => b => c => d => a*b*c*d)
, o que com seu código ainda dá issoTypeError: Cannot read property '1' of null
… enquanto este funciona stackoverflow.com/a/29123804=> ["a", "b", "c"]
fonte
Você também pode usar o analisador "esprima" para evitar muitos problemas com comentários, espaço em branco e outras coisas na lista de parâmetros.
Funciona mesmo com código como este:
fonte
Eu tentei fazer isso antes, mas nunca encontrei uma maneira prática de fazer isso. Acabei passando um objeto e, em seguida, percorrendo o objeto.
fonte
Não sei se esta solução atende ao seu problema, mas permite redefinir qualquer função que você deseja, sem precisar alterar o código que a utiliza. As chamadas existentes usarão parâmetros posicionados, enquanto a implementação da função pode usar "parâmetros nomeados" (um único parâmetro de hash).
Eu pensei que você iria modificar as definições de funções existentes, por que não ter uma função de fábrica que faça exatamente o que você deseja:
Espero que ajude.
fonte
A maneira correta de fazer isso é usar um analisador JS. Aqui está um exemplo usando bolota .
O código aqui encontra os nomes dos três parâmetros (formais) da função
f
. Fá-lo por alimentaçãof
emacorn.parse()
.fonte
Não sei como obter uma lista dos parâmetros, mas você pode fazer isso para obter quantos espera.
fonte
function gotcha (a, b = false, c) {}; alert(gotcha.length)
Tomando a resposta de @ jack-allan, modifiquei ligeiramente a função para permitir propriedades padrão do ES6, como:
ainda voltar
[ 'a', 'b' ]
fonte
Como eu normalmente faço isso:
Você pode até referenciar os argumentos com o nome de funções como:
Espero que isto ajude!
fonte
var args = name.arguments; console.log('I WANNa SEE', args);
output algo como "{arg1: {...}, arg2: 'string'}"? Isso pode esclarecer as coisas:(function fn (arg, argg, arrrrgggg) { console.log('#fn:', fn.arguments, Object.keys(fn.arguments)); }); fn('Huh...?', 'Wha...?', 'Magic...?');
. Os argumentos de função são um objeto semelhante a 'Matriz', com índices enumeráveis. Eu não acho que um mapeamento de hash seja possível, mas você poderia passar um literal de objeto no qual é uma boa prática se você tiver mais de 4 parâmetros de qualquer maneira.fonte
A resposta para isso requer 3 etapas:
argValues
). Isso é simples, pois estará disponível comoarguments
dentro da função.argNames
). Isso não é tão fácil e requer a análise da função. Em vez de fazer a regex complexa e se preocupar com casos extremos (parâmetros padrão, comentários, ...), você pode usar uma biblioteca como babylon que analisará a função em uma árvore de sintaxe abstrata a partir da qual você pode obter os nomes dos parâmetros.O código será assim
e o objeto registrado será
E aqui está um exemplo de trabalho https://tonicdev.com/5763eb77a945f41300f62a79/5763eb77a945f41300f62a7a
fonte
fonte
Wow tantas respostas já .. Eu tenho certeza que isso é enterrado. Mesmo assim, achei que isso poderia ser útil para alguns.
Eu não estava totalmente satisfeito com as respostas escolhidas, pois no ES6 não funciona bem com os valores padrão. E também não fornece as informações de valor padrão. Eu também queria uma função leve que não dependa de uma lib externa.
Essa função é muito útil para fins de depuração, por exemplo: registro chamado function com seus parâmetros, valores de parâmetros e argumentos padrão.
Eu gastei algum tempo nisso ontem, decifrando o RegExp certo para resolver esse problema e foi isso que eu vim com. Funciona muito bem e estou muito satisfeito com o resultado:
Como você pode ver, alguns nomes de parâmetros desaparecem porque o transpiler Babel os remove da função. Se você executasse isso no NodeJS mais recente, ele funcionaria conforme o esperado (os resultados comentados são do NodeJS).
Outra observação, conforme declarado no comentário, é que isso não funciona com as funções de seta embutida como um valor padrão. Isso simplesmente torna muito complexo extrair os valores usando um RegExp.
Informe-me se isso foi útil para você! Gostaria de ouvir algum feedback!
fonte
Vou dar um pequeno exemplo abaixo:
fonte
Este pacote usa reformulação para criar um AST e, em seguida, os nomes dos parâmetros são coletados, permitindo suportar a correspondência de padrões, argumentos padrão, funções de seta e outros recursos do ES6.
https://www.npmjs.com/package/es-arguments
fonte
Modifiquei a versão obtida do AngularJS que implementa um mecanismo de injeção de dependência para funcionar sem o Angular. Também atualizei o
STRIP_COMMENTS
regex para trabalharECMA6
, para que ele suporte coisas como valores padrão na assinatura.fonte
Você pode acessar os valores do argumento passados para uma função usando a propriedade "argumentos".
fonte
arguments
propriedade de uma instância de função foi descontinuado. Veja developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…É bem fácil.
No início, há um objeto obsoleto
arguments.callee
- uma referência à função chamada. No segundo, se você tiver uma referência à sua função, poderá obter facilmente sua representação textual. Na terceira, se você chamar sua função como construtor, também poderá ter um link via yourObject.constructor. Nota: a primeira solução foi preterida. Se você não puder usá-la, também deve pensar na arquitetura do seu aplicativo. Se você não precisar de nomes exatos de variáveis, use dentro de uma variável interna da funçãoarguments
sem nenhuma mágica.https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee
Todos eles chamarão toString e substituirão por re para que possamos criar um auxiliar:
Alguns exemplos:
Divirta-se com JS!
UPD: Jack Allan recebeu uma solução um pouco melhor, na verdade. GJ Jack!
fonte
SomeFuncName
vez dearguments.callee
(ambos apontam para o próprio objeto de função).Qualquer que seja a solução, ela não deve quebrar funções estranhas, cuja
toString()
aparência é igualmente estranha:Além disso, por que usar expressões regulares complexas? Isso pode ser feito como:
Isso funciona em todos os lugares com todas as funções, e o único regex é a remoção de espaços em branco que nem sequer processa toda a cadeia de caracteres devido ao
.split
truque.fonte
Ok, então uma pergunta antiga com muitas respostas adequadas. aqui está minha oferta que não usa regex, exceto a tarefa servil de remover espaços em branco. (Devo observar que a função "strips_comments", na verdade, os separa, em vez de removê-los fisicamente. É porque eu o uso em outro lugar e, por várias razões, precisamos dos locais dos tokens originais sem comentários para permanecer intactos)
É um bloco de código bastante longo, pois essa colagem inclui uma mini estrutura de teste.
fonte
Aqui está uma maneira:
Nota: Isso funciona apenas em navegadores compatíveis
arguments.callee
.fonte
Nota: se você deseja usar a destruição de parâmetros do ES6 com a solução superior, adicione a seguinte linha.
fonte
imagem do valor da sequência de parâmetros da função dinamicamente a partir do JSON . Como item.product_image2 é uma string de URL, é necessário colocá-lo entre aspas quando você chama o parâmetro changeImage inside.
My Function Onclick
My Function
fonte