¿Hay alguna manera de obtener los nombres de los parámetros de función de una función dinámicamente?
Digamos que mi función se ve así:
function doSomething(param1, param2, .... paramN){
// fill an array with the parameter name and value
// some other code
}
Ahora, ¿cómo obtendría una lista de los nombres de los parámetros y sus valores en una matriz desde dentro de la función?
javascript
reflection
function-parameter
vikasde
fuente
fuente
function doSomething(...args) { /*use args*/}
Respuestas:
La siguiente función devolverá una matriz de los nombres de los parámetros de cualquier función pasada.
Ejemplo de uso:
Editar :
Con la invención del ES6, esta función puede activarse mediante parámetros predeterminados. Aquí hay un truco rápido que debería funcionar en la mayoría de los casos:
Digo la mayoría de los casos porque hay algunas cosas que lo harán tropezar
Editar : también noto que vikasde quiere los valores de los parámetros en una matriz también. Esto ya se proporciona en una variable local llamada argumentos.
Extracto de https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments :
El objeto de argumentos no es una matriz. Es similar a una matriz, pero no tiene ninguna propiedad de matriz, excepto la longitud. Por ejemplo, no tiene el método pop. Sin embargo, se puede convertir en una matriz real:
Si hay genéricos de matriz disponibles, se puede usar lo siguiente en su lugar:
fuente
var fn = function(a /* fooled you)*/,b){};
dará como resultado["a", "/*", "fooled", "you"]
A continuación se muestra el código tomado de AngularJS que utiliza la técnica para su mecanismo de inyección de dependencia.
Y aquí hay una explicación tomada de http://docs.angularjs.org/tutorial/step_05
fuente
annotate = angular.injector.$$annotate
Aquí hay una solución actualizada que intenta abordar todos los casos límite mencionados anteriormente de manera compacta:
Salida de prueba abreviada (los casos de prueba completos se adjuntan a continuación):
Mostrar fragmento de código
fuente
return (func+'') .replace(/[/][/].*$/mg,'') // strip single-line comments (line-ending sensitive, so goes first) .replace(/\s+/g,'') // remove whitespace
func + ''
conFunction.toString.call(func)
para defenderse del caso cuando la función tiene una implementación personalizada .toString ()..split(/\)[\{=]/, 1)[0]
({ a, b, c })
) en todos los parámetros dentro de la desestructuración. Para mantener intactos los objetos desestructurados, cambie el último.split
a:.split(/,(?![^{]*})/g)
La solución que es menos propensa a errores de espacios y comentarios sería:
fuente
Muchas de las respuestas aquí usan expresiones regulares, esto está bien, pero no maneja las nuevas adiciones al lenguaje demasiado bien (como las funciones de flecha y las clases). También es de destacar que si usa alguna de estas funciones en el código minimizado, irá 🔥. Utilizará cualquier nombre minificado. Angular evita esto al permitirle pasar una serie ordenada de cadenas que coincida con el orden de los argumentos al registrarlos en el contenedor DI. Entonces con la solución:
Esto maneja el problema de análisis original y algunos tipos de funciones más (por ejemplo, funciones de flecha). Aquí hay una idea de lo que puede y no puede manejar como es:
Dependiendo de lo que quieras usar para ES6 Proxies y la desestructuración puede ser tu mejor opción. Por ejemplo, si desea utilizarlo para la inyección de dependencia (utilizando los nombres de los parámetros), puede hacerlo de la siguiente manera:
No es el solucionador más avanzado que existe, pero da una idea de cómo puede usar un Proxy para manejarlo si desea usar el analizador args para una DI simple. Sin embargo, hay una leve advertencia en este enfoque. Necesitamos usar tareas de desestructuración en lugar de parámetros normales. Cuando pasamos el proxy del inyector, la desestructuración es lo mismo que llamar al captador del objeto.
Esto genera lo siguiente:
Está conectado toda la aplicación. Lo mejor es que la aplicación es fácil de probar (puede crear una instancia de cada clase y aprobar simulacros / trozos / etc.). Además, si necesita intercambiar implementaciones, puede hacerlo desde un solo lugar. Todo esto es posible gracias a los objetos JS Proxy.
Nota: Hay mucho trabajo por hacer antes de que esté listo para su uso en producción, pero da una idea de cómo se vería.
Es un poco tarde en la respuesta, pero puede ayudar a otros que están pensando en lo mismo. 👍
fuente
Sé que esta es una vieja pregunta, pero los principiantes han estado copiando esto como si fuera una buena práctica en cualquier código. La mayoría de las veces, tener que analizar la representación de cadena de una función para usar los nombres de sus parámetros solo esconde una falla en la lógica del código.
Los parámetros de una función se almacenan realmente en un objeto tipo matriz llamado
arguments
, donde está el primer argumentoarguments[0]
, el segundoarguments[1]
y así sucesivamente. Escribir nombres de parámetros entre paréntesis puede verse como una sintaxis abreviada. Esta:...es lo mismo que:
Las variables mismas se almacenan en el alcance de la función, no como propiedades en un objeto. No hay forma de recuperar el nombre del parámetro a través del código, ya que es simplemente un símbolo que representa la variable en lenguaje humano.
Siempre consideré la representación de cadena de una función como una herramienta para fines de depuración, especialmente debido a este
arguments
objeto tipo matriz. No está obligado a dar nombres a los argumentos en primer lugar. Si intenta analizar una función en cadena, en realidad no le informa sobre los parámetros adicionales sin nombre que podría tomar.Aquí hay una situación aún peor y más común. Si una función tiene más de 3 o 4 argumentos, podría ser lógico pasarle un objeto en su lugar, con lo cual es más fácil trabajar.
En este caso, la función en sí podrá leer el objeto que recibe y buscar sus propiedades y obtener sus nombres y valores, pero intentar analizar la representación de cadena de la función solo le dará "obj" para los parámetros, lo cual no es útil en absoluto.
fuente
Function.prototype.toString = function () { return 'function () { [native code] }'; };
Proxy
( trampa de constructor y aplicación de trampa ) yReflection
para manejar DI para algunos de mis módulos. Es razonablemente sólido.Dado que JavaScript es un lenguaje de secuencias de comandos, creo que su introspección debería admitir la obtención de nombres de parámetros de funciones. La utilización de esa funcionalidad es una violación de los primeros principios, por lo que decidí explorar el problema más a fondo.
Eso me llevó a esta pregunta, pero no a soluciones integradas. Lo que me llevó a esta respuesta que explica que
arguments
solo está en desuso fuera de la función, por lo que ya no podemos usarmyFunction.arguments
u obtener:Es hora de remangarse y ponerse a trabajar:
⭐ La recuperación de parámetros de función requiere un analizador sintáctico porque las expresiones complejas como
4*(5/3)
pueden usarse como valores predeterminados. Entonces, la respuesta de Gaafar o la respuesta de James Drew son hasta ahora los mejores enfoques.Probé la babilonia y Esprima analizadores pero por desgracia no pueden funciones anónimas independientes de análisis sintáctico, como en punta a cabo en la respuesta de Mateusz Charytoniuk . Sin embargo, descubrí otra solución al rodear el código entre paréntesis, para no cambiar la lógica:
Las nuevas líneas evitan problemas con
//
(comentarios de una sola línea).⭐ Si un analizador no está disponible, la siguiente mejor opción es usar una técnica probada y verdadera como las expresiones regulares del inyector de dependencia de Angular.js. Combiné una versión funcional de la respuesta de Lambder con la respuesta de humbletim y agregué un
ARROW
booleano opcional para controlar si las expresiones regulares permiten las funciones de flecha de grasa ES6.Aquí hay dos soluciones que puse juntas. Tenga en cuenta que estos no tienen lógica para detectar si una función tiene una sintaxis válida, solo extraen los argumentos. Esto generalmente está bien, ya que generalmente pasamos funciones analizadas para
getArguments()
que su sintaxis ya sea válida.Intentaré seleccionar estas soluciones lo mejor que pueda, pero sin el esfuerzo de los mantenedores de JavaScript, esto seguirá siendo un problema abierto.
Versión de Node.js (no se puede ejecutar hasta que StackOverflow sea compatible con Node.js):
Ejemplo de trabajo completo:
https://repl.it/repls/SandybrownPhonyAngles
Versión del navegador (tenga en cuenta que se detiene en el primer valor predeterminado complejo):
Ejemplo de trabajo completo:
https://repl.it/repls/StupendousShowyOffices
fuente
He leído la mayoría de las respuestas aquí, y me gustaría agregar mi frase.
o
o para una función de una línea en ECMA6
__
Digamos que tienes una función
El siguiente código regresará
"abc,def,ghi,jkl"
Ese código también funcionará con la configuración de una función que dio Camilo Martin :
También con el comentario de Bubersson sobre la respuesta de Jack Allan :
__
Explicación
new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')
Esto crea una expresión regular con el
new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')
. Tengo que usarlonew RegExp
porque estoy inyectando una variable (Function.name
el nombre de la función a la que se dirige) en el RegExp.Ejemplo Si el nombre de la función es "foo" (
function foo()
), lo será RegExp/foo\s*\((.*?)\)/
.Function.toString().replace(/\n/g, '')
Luego, convierte toda la función en una cadena y elimina todas las líneas nuevas. Eliminar las nuevas líneas ayuda con la configuración de la función que dio Camilo Martin .
.exec(...)[1]
Esta es la
RegExp.prototype.exec
función Básicamente coincide con el exponente regular (new RegExp()
) en la cadena (Function.toString()
). Luego[1]
, devolverá el primer grupo de captura encontrado en el exponente regular ((.*?)
)..replace(/\/\*.*?\*\//g, '').replace(/ /g, '')
Esto eliminará todos los comentarios dentro
/*
y*/
, y eliminará todos los espacios.Esto ahora también admite la lectura y comprensión de las
=>
funciones de flecha ( ), como por ejemplof = (a, b) => void 0;
, en la queFunction.toString()
volvería en(a, b) => void 0
lugar de las funciones normalesfunction f(a, b) { return void 0; }
. La expresión regular original habría arrojado un error en su confusión, pero ahora se tiene en cuenta.El cambio fue de
new RegExp(Function.name+'\\s*\\((.*?)\\)')
(/Function\s*\((.*?)\)/
) anew RegExp('(?:'+Function.name+'\\s*|^)\\((.*?)\\)')
(/(?:Function\s*|^)\((.*?)\)/
)Si desea convertir todos los parámetros en una matriz en lugar de una cadena separada por comas, al final simplemente agregue
.split(',')
.fuente
f = (a, b) => void 0
; elgetParameters(f)
reciboTypeError: Cannot read property '1' of null
getParameters(a => b => c => d => a*b*c*d)
, que con su código todavía da esoTypeError: Cannot read property '1' of null
... mientras que este funciona stackoverflow.com/a/29123804=> ["a", "b", "c"]
fuente
También puede usar el analizador "esprima" para evitar muchos problemas con comentarios, espacios en blanco y otras cosas dentro de la lista de parámetros.
Funciona incluso con un código como este:
fuente
He intentado hacer esto antes, pero nunca encontré una forma práctica de hacerlo. Terminé pasando un objeto y luego recorriéndolo.
fuente
No sé si esta solución se adapta a su problema, pero le permite redefinir la función que desee, sin tener que cambiar el código que la utiliza. Las llamadas existentes usarán parámetros posicionados, mientras que la implementación de la función puede usar "parámetros nombrados" (un solo parámetro hash).
Pensé que de todos modos modificarías las definiciones de funciones existentes, entonces, ¿por qué no tener una función de fábrica que haga justo lo que quieres?
Espero eso ayude.
fuente
La forma correcta de hacer esto es usar un analizador JS. Aquí hay un ejemplo usando bellota .
El código aquí encuentra los nombres de los tres parámetros (formales) de la función
f
. Lo hace mediante la alimentaciónf
enacorn.parse()
.fuente
No sé cómo obtener una lista de los parámetros, pero puede hacer esto para obtener cuántos espera.
fuente
function gotcha (a, b = false, c) {}; alert(gotcha.length)
Tomando la respuesta de @ jack-allan, modifiqué la función ligeramente para permitir propiedades predeterminadas de ES6 como:
aún regresar
[ 'a', 'b' ]
fuente
Cómo lo hago típicamente:
Incluso puede hacer referencia a los argumentos por el nombre de las funciones como:
¡Espero que esto ayude!
fuente
var args = name.arguments; console.log('I WANNa SEE', args);
generara algo como "{arg1: {...}, arg2: 'string'}"? Esto podría aclarar las cosas:(function fn (arg, argg, arrrrgggg) { console.log('#fn:', fn.arguments, Object.keys(fn.arguments)); }); fn('Huh...?', 'Wha...?', 'Magic...?');
. Los argumentos de función son un objeto tipo 'matriz', que tiene índices enumerables. No creo que sea posible un hash-mapping, pero podría pasar un Object-literal en el que es una buena práctica si tiene más de 4 parámetros de todos modos.fuente
La respuesta a esto requiere 3 pasos:
argValues
). Esto es sencillo ya que estará disponiblearguments
dentro de la función.argNames
). Esto no es tan fácil y requiere analizar la función. En lugar de hacer la expresión regular compleja usted mismo y preocuparse por los casos límite (parámetros predeterminados, comentarios, ...), puede usar una biblioteca como babylon que analizará la función en un árbol de sintaxis abstracta del que puede obtener los nombres de los parámetros.El código será así
y el objeto registrado será
Y aquí hay un ejemplo de trabajo https://tonicdev.com/5763eb77a945f41300f62a79/5763eb77a945f41300f62a7a
fuente
fuente
Wow tantas respuestas ya ... Estoy bastante seguro de que esto se entierra. Aun así, pensé que esto podría ser útil para algunos.
No estaba completamente satisfecho con las respuestas elegidas, ya que en ES6 no funciona bien con los valores predeterminados. Y tampoco proporciona la información del valor predeterminado. También quería una función ligera que no dependa de una lib externa.
Esta función es muy útil para propósitos de depuración, por ejemplo: registro llamado función con sus parámetros, valores predeterminados de parámetros y argumentos.
Ayer pasé un tiempo en esto, descifrando el RegExp correcto para resolver este problema y esto es lo que se me ocurrió. Funciona muy bien y estoy muy satisfecho con el resultado:
Como puede ver, algunos de los nombres de los parámetros desaparecen porque el transpilador de Babel los elimina de la función. Si ejecuta esto en el último NodeJS, funciona como se esperaba (Los resultados comentados son de NodeJS).
Otra nota, como se indica en el comentario, es que no funciona con las funciones de flecha en línea como valor predeterminado. Esto simplemente hace que sea demasiado complejo extraer los valores usando un RegExp.
¡Avísame si esto fue útil para ti! Me encantaría escuchar algunos comentarios!
fuente
Te daré un breve ejemplo a continuación:
fuente
Este paquete utiliza la refundición para crear un AST y luego los nombres de los parámetros se recopilan de ellos, esto le permite admitir la coincidencia de patrones, argumentos predeterminados, funciones de flecha y otras características de ES6.
https://www.npmjs.com/package/es-arguments
fuente
He modificado la versión tomada de AngularJS que implementa un mecanismo de inyección de dependencia para trabajar sin Angular. También he actualizado la
STRIP_COMMENTS
expresión regular para trabajarECMA6
, por lo que admite cosas como los valores predeterminados en la firma.fuente
Puede acceder a los valores de argumento pasados a una función utilizando la propiedad "argumentos".
fuente
arguments
propiedad de una instancia de función está en desuso. Ver developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…Es muy facil
En el primero hay una obsoleta
arguments.callee
, una referencia a la función llamada. En el segundo, si tiene una referencia a su función, puede obtener fácilmente su representación textual. En el tercero, si llama a su función como constructor, también puede tener un enlace a través de yourObject.constructor. NB: La primera solución está en desuso, por lo que si no puede no usarla, también debe pensar en la arquitectura de su aplicación. Si no necesita nombres de variables exactos, simplemente use dentro de una variable interna de funciónarguments
sin ninguna magia.https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee
Todos llamarán a toString y los reemplazarán con re para que podamos crear un ayudante:
Algunos ejemplos:
¡Disfruta con JS!
UPD: Jack Allan recibió una solución un poco mejor en realidad. GJ Jack!
fuente
SomeFuncName
lugar dearguments.callee
(ambos apuntan al objeto de función en sí).Cualquiera sea la solución, no debe interrumpirse en funciones extrañas, cuyo
toString()
aspecto es tan extraño:Además, ¿por qué usar expresiones regulares complejas? Esto se puede hacer como:
Esto funciona en todas partes con cada función, y la única expresión regular es la eliminación de espacios en blanco que ni siquiera procesa toda la cadena debido al
.split
truco.fuente
Ok, entonces una vieja pregunta con muchas respuestas adecuadas. Aquí está mi oferta que no utiliza expresiones regulares, a excepción de la tarea doméstica de eliminar espacios en blanco. (Debo señalar que la función "strips_comments" en realidad los separa, en lugar de eliminarlos físicamente. Eso es porque lo uso en otro lugar y por varias razones necesito que las ubicaciones de los tokens originales sin comentarios se mantengan intactos)
Es un bloque de código bastante largo ya que este pegado incluye un mini marco de prueba.
fuente
Aquí hay una manera:
Nota: Esto solo funciona en navegadores compatibles
arguments.callee
.fuente
Nota: si desea utilizar la desestructuración de parámetros ES6 con la solución superior, agregue la siguiente línea.
fuente
Imagen de valor de cadena de parámetro de función dinámicamente desde JSON . Como item.product_image2 es una cadena de URL, debe ponerla entre comillas cuando llame a changeImage dentro del parámetro.
My Function Onclick
Mi funcion
fuente