¿Puedo obtener el nombre de la función actualmente en ejecución en JavaScript?

185

Es posible hacer esto:

myfile.js:
function foo() {
    alert(<my-function-name>);
    // pops-up "foo"
    // or even better: "myfile.js : foo"
}

Tengo los marcos Dojo y jQuery en mi pila, así que si alguno de ellos lo hace más fácil, están disponibles.

Sprugman
fuente

Respuestas:

196

En ES5 y superior, no hay acceso a esa información.

En versiones anteriores de JS puede obtenerlo usando arguments.callee.

Sin embargo, es posible que deba analizar el nombre, ya que probablemente incluirá algo de basura adicional. Sin embargo, en algunas implementaciones simplemente puede obtener el nombre usando arguments.callee.name.

Analizando:

function DisplayMyName() 
{
   var myName = arguments.callee.toString();
   myName = myName.substr('function '.length);
   myName = myName.substr(0, myName.indexOf('('));

   alert(myName);
}

Fuente: Javascript: obtener el nombre de la función actual .

Mate
fuente
En realidad, en realidad a prestar más atención a su pregunta, parece que es posible que desee la basura adicional :)
Matt
23
@ Andrew - Tienes razón, debería haber dicho eso. Fue una copia / pegado / limpieza rápida de algo que ya había marcado, y un descuido de mi parte. Gracias por agregarlo a mi publicación.
Matt
81
Rompe en modo estricto ES5.
Raynos 01 de
44
Oh ... es por eso que la gente siempre me pega en la velocidad para responder No había pensado en eso.
Erik Reppen
9
si está utilizando un objeto literal para sus métodos y no tiene un nombre de método real, entonces esto no funcionará como argumentos. callee actúa como una función anónima que no llevará ningún nombre de función. Tendría que asegurarse de agregar ese nombre de función dos veces. Eche un vistazo a este ejemplo de jsfiddle : jsfiddle.net/ncays . Sin embargo, otro problema con esto es que arguments.calleeno está permitido en modo estricto.
hellatan
75

Para funciones no anónimas

function foo()
{ 
    alert(arguments.callee.name)
}

Pero en caso de un controlador de errores, el resultado sería el nombre de la función del controlador de errores, ¿no?

adelante
fuente
2
Funciona muy bien en Chrome. Mucho mejor que la respuesta aceptada.
B Seven
1
Vale la pena tener en cuenta: eslint.org/docs/rules/no-caller > "obsoleto en futuras versiones de JavaScript y su uso está prohibido en ECMAScript 5 mientras está en modo estricto".
Jeremy
44

Todo lo que necesitas es simple. Crear función:

function getFuncName() {
   return getFuncName.caller.name
}

Después de eso, siempre que lo necesite, simplemente use:

function foo() { 
  console.log(getFuncName())
}

foo() 
// Logs: "foo"
Igor Ostroumov
fuente
3
Gracias, esto es mucho más elegante que analizar una cadena.
modle13
1
¡Parece ser la mejor respuesta!
Sergey
Perfecto. Fue entonces cuando JS No Tiene constantes nativas como PHP hace con constantes mágicas ...
stamster
Chrome me da un error de tipo porque la propiedad 'nombre' no existe en la persona que llama. Sin embargo, la inspección reveló que esto funcionó:function getFuncName() { return getFuncName.name }
Tom Anderson
@TomAnderson con su cambio, ahora está obteniendo el nombre en getFuncNamelugar del nombre de la persona que llama.
Mark McKenna
30

De acuerdo con MDN

Advertencia: La quinta edición de ECMAScript (ES5) prohíbe el uso de argumentos.callee () en modo estricto. Evite usar argumentos.callee () dando un nombre a las expresiones de función o utilice una declaración de función donde una función debe llamarse a sí misma.

Como se señaló, esto se aplica solo si su script usa "modo estricto". Esto es principalmente por razones de seguridad y lamentablemente actualmente no hay alternativa para esto.

Laimis
fuente
21

Esto debería hacerlo:

var fn = arguments.callee.toString().match(/function\s+([^\s\(]+)/);
alert(fn[1]);

Para la persona que llama, solo use caller.toString().

Andy E
fuente
8
Esto funcionó para mí, pero creo que hay un error tipográfico en su expresión regular. Tuve que sacar la barra invertida antes del[
declan
44
@declan: sí, tienes razón. Es sorprendente que nadie más haya señalado que en los casi 3 años esta respuesta ha estado aquí :-)
Andy E
@AndyE probablemente nadie lo señaló porque una vez que vemos una expresión regular, entramos en TL; modo DR y buscamos otras respuestas;)
BitTickler
11

Esto tiene que ir en la categoría de "hacks más feos del mundo", pero aquí tienes.

Primero, imprimir el nombre de la función actual (como en las otras respuestas) parece tener un uso limitado para mí, ¡ya que ya sabes cuál es la función!

Sin embargo, averiguar el nombre de la función de llamada podría ser bastante útil para una función de rastreo. Esto es con una expresión regular, pero usar indexOf sería aproximadamente 3 veces más rápido:

function getFunctionName() {
    var re = /function (.*?)\(/
    var s = getFunctionName.caller.toString();
    var m = re.exec( s )
    return m[1];
}

function me() {
    console.log( getFunctionName() );
}

me();
James Hugard
fuente
buena solución, pero la función FYI # que llama no es un desarrollador.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Max Heiber
Conocer el nombre de la función actual puede ser esencial si esa función se está creando dinámicamente desde una base de datos y necesita información de contexto dentro de la función que está codificada para el nombre de la función.
Paul Chernoch
9

Aquí hay una manera que funcionará:

export function getFunctionCallerName (){
  // gets the text between whitespace for second part of stacktrace
  return (new Error()).stack.match(/at (\S+)/g)[1].slice(3);
}

Luego en tus pruebas:

import { expect } from 'chai';
import { getFunctionCallerName } from '../../../lib/util/functions';

describe('Testing caller name', () => {

    it('should return the name of the function', () => {
      function getThisName(){
        return getFunctionCallerName();
      }

      const functionName = getThisName();

      expect(functionName).to.equal('getThisName');
    });

  it('should work with an anonymous function', () => {


    const anonymousFn = function (){
      return getFunctionCallerName();
    };

    const functionName = anonymousFn();

    expect(functionName).to.equal('anonymousFn');
  });

  it('should work with an anonymous function', () => {
    const fnName = (function (){
      return getFunctionCallerName();
    })();

    expect(/\/util\/functions\.js/.test(fnName)).to.eql(true);
  });

});

Tenga en cuenta que la tercera prueba solo funcionará si la prueba se encuentra en / util / functions

Antonio
fuente
7

La getMyNamefunción en el fragmento a continuación devuelve el nombre de la función de llamada. Es un truco y se basa en la no-estándar de función: Error.prototype.stack. Tenga en cuenta que el formato de la cadena devuelta por Error.prototype.stackse implementa de manera diferente en diferentes motores, por lo que esto probablemente no funcionará en todas partes:

function getMyName() {
  var e = new Error('dummy');
  var stack = e.stack
                .split('\n')[2]
                // " at functionName ( ..." => "functionName"
                .replace(/^\s+at\s+(.+?)\s.+/g, '$1' );
                return stack
}

function foo(){
  return getMyName()
}

function bar() {
  return foo()
}

console.log(bar())

Acerca de otras soluciones: arguments.callee no está permitido en modo estricto y noFunction.prototype.caller es estándar y no está permitido en modo estricto .

Max Heiber
fuente
extienda esto para mostrar también la posición en la función y admitir funciones anónimas con: .replace (/ ^ \ s + at \ s (. +?) (?: \ s. *: |:) (. *?): (. * ?))? $ / g, '$ 1 ($ 2: $ 3)')
kofifus
Function.prototype.caller tampoco está permitido en modo estricto.
fijiaaron el
1
Funciona perfecto incluso para funciones de flecha, respuesta subestimada
Hao Wu
3

Otro caso de uso podría ser un despachador de eventos vinculado en tiempo de ejecución:

MyClass = function () {
  this.events = {};

  // Fire up an event (most probably from inside an instance method)
  this.OnFirstRun();

  // Fire up other event (most probably from inside an instance method)
  this.OnLastRun();

}

MyClass.prototype.dispatchEvents = function () {
  var EventStack=this.events[GetFunctionName()], i=EventStack.length-1;

  do EventStack[i]();
  while (i--);
}

MyClass.prototype.setEvent = function (event, callback) {
  this.events[event] = [];
  this.events[event].push(callback);
  this["On"+event] = this.dispatchEvents;
}

MyObject = new MyClass();
MyObject.setEvent ("FirstRun", somecallback);
MyObject.setEvent ("FirstRun", someothercallback);
MyObject.setEvent ("LastRun", yetanothercallback);

La ventaja aquí es que el despachador se puede reutilizar fácilmente y no tiene que recibir la cola de despacho como argumento, sino que viene implícito con el nombre de la invocación ...

Al final, el caso general presentado aquí sería "usar el nombre de la función como argumento para que no tenga que pasarlo explícitamente", y eso podría ser útil en muchos casos, como la devolución de llamada opcional jquery animate (), o en tiempos de espera / intervalos de devolución de llamada (es decir, solo pasa un NOMBRE de función).

Sampiolin
fuente
2

El nombre de la función actual y cómo se puede obtener parece haber cambiado en los últimos 10 años, ya que se hizo esta pregunta.

Ahora, al no ser un desarrollador web profesional que conozca todas las historias de todos los navegadores que hayan existido, así es como funciona para mí en un navegador Chrome 2019:

function callerName() {
    return callerName.caller.name;
}
function foo() {
    let myname = callerName();
    // do something with it...
}

Algunas de las otras respuestas se encontraron con algunos errores de Chrome sobre el código JavaScript estricto y otras cosas.

BitTickler
fuente
1

Dado que ha escrito una función llamada fooy sabe que es así, myfile.js¿por qué necesita obtener esta información dinámicamente?

Dicho esto, puede usar arguments.callee.toString()dentro de la función (esta es una representación de cadena de toda la función) y expresar el valor del nombre de la función.

Aquí hay una función que escupirá su propio nombre:

function foo() {
    re = /^function\s+([^(]+)/
    alert(re.exec(arguments.callee.toString())[1]);             
}
Andrew Hare
fuente
55
Estoy trabajando en un controlador de errores y quiero informar la función de llamada.
sprugman
1

Una combinación de las pocas respuestas que he visto aquí. (Probado en FF, Chrome, IE11)

function functionName() 
{
   var myName = functionName.caller.toString();
   myName = myName.substr('function '.length);
   myName = myName.substr(0, myName.indexOf('('));
   return myName;
}

function randomFunction(){
    var proof = "This proves that I found the name '" + functionName() + "'";
    alert(proof);
}

Llamar a randomFunction () alertará a una cadena que contiene el nombre de la función.

Demostración de JS Fiddle: http://jsfiddle.net/mjgqfhbe/

buddamus
fuente
1

La información es actual en el año 2016.


Resultados para la declaración de función

Resultado en la Opera

>>> (function func11 (){
...     console.log(
...         'Function name:',
...         arguments.callee.toString().match(/function\s+([_\w]+)/)[1])
... })();
... 
... (function func12 (){
...     console.log('Function name:', arguments.callee.name)
... })();
Function name:, func11
Function name:, func12

Resultado en el Chrome

(function func11 (){
    console.log(
        'Function name:',
        arguments.callee.toString().match(/function\s+([_\w]+)/)[1])
})();

(function func12 (){
    console.log('Function name:', arguments.callee.name)
})();
Function name: func11
Function name: func12

Resultado en el NodeJS

> (function func11 (){
...     console.log(
.....         'Function name:',
.....         arguments.callee.toString().match(/function\s+([_\w]+)/)[1])
... })();
Function name: func11
undefined
> (function func12 (){
...     console.log('Function name:', arguments.callee.name)
... })();
Function name: func12

No funciona en Firefox. No probado en el IE y The Edge.


Resultados para expresiones de funciones

Resultado en el NodeJS

> var func11 = function(){
...     console.log('Function name:', arguments.callee.name)
... }; func11();
Function name: func11

Resultado en el Chrome

var func11 = function(){
    console.log('Function name:', arguments.callee.name)
}; func11();
Function name: func11

No funciona en Firefox, Opera. No probado en el IE y The Edge.

Notas:

  1. La función anónima no tiene sentido verificar.
  2. Entorno de prueba

~ $ google-chrome --version
Google Chrome 53.0.2785.116           
~ $ opera --version
Opera 12.16 Build 1860 for Linux x86_64.
~ $ firefox --version
Mozilla Firefox 49.0
~ $ node
node    nodejs  
~ $ nodejs --version
v6.8.1
~ $ uname -a
Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
PADYMKO
fuente
1
(function f() {
    console.log(f.name);  //logs f
})();

Variación mecanografiada:

function f1() {} 
function f2(f:Function) {
   console.log(f.name);
}

f2(f1);  //Logs f1

Nota solo disponible en motores compatibles con ES6 / ES2015. Para más ver

Viejo
fuente
0

Aquí hay una línea:

    arguments.callee.toString().split('\n')[0].substr('function '.length).replace(/\(.*/, "").replace('\r', '')

Me gusta esto:

    function logChanges() {
      let whoami = arguments.callee.toString().split('\n')[0].substr('function '.length).replace(/\(.*/, "").replace('\r', '');
      console.log(whoami + ': just getting started.');
    }
VRMBW
fuente
0

Esta es una variante de la respuesta de Igor Ostroumov :

Si desea utilizarlo como valor predeterminado para un parámetro, debe considerar una llamada de segundo nivel a 'llamante':

function getFunctionsNameThatCalledThisFunction()
{
  return getFunctionsNameThatCalledThisFunction.caller.caller.name;
}

Esto permitiría dinámicamente una implementación reutilizable en múltiples funciones.

function getFunctionsNameThatCalledThisFunction()
{
  return getFunctionsNameThatCalledThisFunction.caller.caller.name;
}

function bar(myFunctionName = getFunctionsNameThatCalledThisFunction())
{ 
  alert(myFunctionName);
}

// pops-up "foo"
function foo()
{
  bar();
}

function crow()
{
  bar();
}

foo();
crow();

Si también desea el nombre del archivo, aquí está esa solución usando la respuesta de F-3000 en otra pregunta:

function getCurrentFileName()
{
  let currentFilePath = document.scripts[document.scripts.length-1].src 
  let fileName = currentFilePath.split('/').pop() // formatted to the OP's preference

  return fileName 
}

function bar(fileName = getCurrentFileName(),  myFunctionName = getFunctionsNameThatCalledThisFunction())
{
  alert(fileName + ' : ' + myFunctionName);
}

// or even better: "myfile.js : foo"
function foo()
{
  bar();
}
SMAG
fuente
-1

Tratar:

alert(arguments.callee.toString());
Deniz Dogan
fuente
3
Eso devolvería toda la función como una cadena
Andy E
-7

La respuesta es corta: alert(arguments.callee.name);

Albahaca
fuente
12
"nom" es "nombre" en francés. ¿Este tipo de detalle cambia entre las versiones de idioma de los navegadores? No lo creo.
argyle