Posso obter o nome da função atualmente em execução em JavaScript?

185

É possível fazer isso:

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

Eu tenho as estruturas Dojo e jQuery na minha pilha, portanto, se alguma delas facilitar, elas estarão disponíveis.

sprugman
fonte

Respostas:

196

No ES5 e acima, não há acesso a essas informações.

Nas versões mais antigas do JS, você pode obtê-lo usando arguments.callee.

Talvez você precise analisar o nome, pois provavelmente incluirá algum lixo extra. Porém, em algumas implementações, você pode simplesmente obter o nome usando arguments.callee.name.

Análise:

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

   alert(myName);
}

Fonte: Javascript - obtenha o nome da função atual .

Matt
fonte
Na verdade, na verdade, prestar mais atenção à sua pergunta, parece que você pode querer o lixo extra :)
Matt
23
@ Andrew - Você está certo, eu deveria ter afirmado isso. Foi uma cópia / pasta / limpeza rápida de algo que eu já tinha marcado e uma supervisão da minha parte. Obrigado por adicioná-lo ao meu post.
Matt
81
Quebra no modo estrito ES5.
Raynos
4
Oh ... é por isso que as pessoas sempre me batem na velocidade de responder. Eu não tinha pensado nisso.
precisa
9
se você estiver usando um literal de objeto para seus métodos e nenhum nome de método real, isso não funcionará como argumentos.callee age como uma função anônima que não carregará nenhum nome de função. Você precisaria adicionar o nome da função duas vezes. Veja este exemplo do jsfiddle : jsfiddle.net/ncays . outro problema com isso, porém, é que arguments.calleenão é permitido no modo estrito.
28813 hellatan
75

Para funções não anônimas

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

Mas no caso de um manipulador de erros, o resultado seria o nome da função do manipulador de erros, não?

fforw
fonte
2
Funciona muito bem no Chrome. Resposta muito melhor do que aceita.
B Sete
1
Vale lembrar: eslint.org/docs/rules/no-caller > "descontinuado em versões futuras do JavaScript e seu uso é proibido no ECMAScript 5, no modo estrito".
Jeremy
44

Tudo o que você precisa é simples. Criar função:

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

Depois disso, sempre que precisar, basta usar:

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

foo() 
// Logs: "foo"
Igor Ostroumov
fonte
3
Obrigado, isso é muito mais elegante do que analisar uma string.
Modle13
1
Parece ser a melhor resposta!
Sergey
Perfeito. Foi quando JS não tem constantes nativas como PHP faz com constantes mágicas ...
stamster
O Chrome me deu um erro de tipo porque a propriedade 'name' não existe no chamador. No entanto, a inspeção revelou que funcionou:function getFuncName() { return getFuncName.name }
Tom Anderson
@ TomAnderson com sua alteração, agora você está recebendo o nome do getFuncNamechamador e não o nome do chamador.
Mark McKenna
30

De acordo com a MDN

Aviso: A 5ª edição do ECMAScript (ES5) proíbe o uso de argument.callee () no modo estrito. Evite usar argument.callee (), dando um nome às expressões de função ou use uma declaração de função em que uma função deve se chamar.

Conforme observado, isso se aplica apenas se o seu script usar "modo estrito". Isso ocorre principalmente por razões de segurança e, infelizmente, atualmente não há alternativa para isso.

Laimis
fonte
21

Isso deve servir:

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

Para o chamador, basta usar caller.toString().

Andy E
fonte
8
Isso funcionou para mim, mas acho que há um erro de digitação no seu regexp. Eu tive que remover a barra invertida antes do[
declin
4
@ declan: sim, você está certo. É surpreendente que ninguém mais tem apontado que nos quase 3 anos esta resposta tem sido aqui :-)
Andy E
@AndyE provavelmente ninguém apontou porque uma vez que vemos um regexp, entramos TL; DR modo e olhar para outras respostas;)
BitTickler
11

Isso deve estar na categoria de "hacks mais feios do mundo", mas aqui está.

Primeiro, imprimir o nome da função atual (como nas outras respostas) parece ter uso limitado para mim, já que você já sabe qual é a função!

No entanto, descobrir o nome da função de chamada pode ser bastante útil para uma função de rastreamento. Isso é com um regexp, mas usar indexOf seria cerca de 3x mais 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
fonte
arrefecer solução, mas FYI Função # chamador é não-padrão developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Max Heiber
O conhecimento do nome da função atual pode ser essencial se essa função estiver sendo criada dinamicamente a partir de um banco de dados e precisar de informações de contexto dentro da função que está codificada para o nome da função.
Paul Chernoch
9

Aqui está uma maneira 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);
}

Então nos seus testes:

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

});

Observe que o terceiro teste só funcionará se o teste estiver localizado em / util / functions

Anthony
fonte
7

A getMyNamefunção no trecho abaixo retorna o nome da função de chamada. É um hack e depende de não-padrão recurso: Error.prototype.stack. Observe que o formato da string retornada por Error.prototype.stacké implementado de maneira diferente em diferentes mecanismos, portanto, isso provavelmente não funcionará em qualquer lugar:

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())

Sobre outras soluções: arguments.callee não é permitido no modo estrito e nãoFunction.prototype.caller é padrão e não é permitido no modo estrito .

Max Heiber
fonte
estenda isso para mostrar também a posição na função e oferecer suporte a funções anônimas com: .replace (/ ^ \ s + at \ s (. +?) (?: \ s. *: |:) (. *?): (. * ?))? $ / g, '$ 1 ($ 2: $ 3)')
kofifus
Function.prototype.caller também não é permitido no modo estrito.
Fijiaaron 26/11/18
1
Funciona mesmo perfeito para funções de direção, resposta subestimado
Hao Wu
3

Outro caso de uso pode ser um distribuidor de eventos vinculado no tempo de execução:

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

A vantagem aqui é que o despachante pode ser facilmente reutilizado e não precisa receber a fila de despacho como argumento. Em vez disso, é implícito com o nome da chamada ...

No final, o caso geral apresentado aqui seria "usar o nome da função como argumento para que você não precise transmiti-lo explicitamente", e isso pode ser útil em muitos casos, como o retorno de chamada opcional jquery animate (), ou em retornos de chamada de intervalos / intervalos (ou seja, você passa apenas uma função NAME).

Sampiolin
fonte
2

O nome da função atual e como ela pode ser obtida parece ter mudado nos últimos 10 anos, desde que essa pergunta foi feita.

Agora, não sendo um desenvolvedor profissional da Web que conhece todas as histórias de todos os navegadores que já existiram, eis como funciona para mim em um navegador Chrome de 2019:

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

Algumas das outras respostas encontraram alguns erros de cromo sobre código estrito de javascript e outros enfeites.

BitTickler
fonte
1

Desde que você escreveu uma função nomeada fooe sabe que está nela, myfile.jspor que você precisa obter essas informações dinamicamente?

Dito isto, você pode usar arguments.callee.toString()dentro da função (esta é uma representação de string de toda a função) e regexar o valor do nome da função.

Aqui está uma função que cuspirá seu próprio nome:

function foo() {
    re = /^function\s+([^(]+)/
    alert(re.exec(arguments.callee.toString())[1]);             
}
Andrew Hare
fonte
5
Estou trabalhando em um manipulador de erros e quero relatar a função de chamada.
Sprugman
1

Uma combinação das poucas respostas que eu vi aqui. (Testado em 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);
}

Chamar randomFunction () alertará uma string que contém o nome da função.

Demonstração JS Fiddle: http://jsfiddle.net/mjgqfhbe/

buddamus
fonte
1

As informações são atuais no ano de 2016.


Resultados para declaração de função

Resultado na Ópera

>>> (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 no 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 no 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

Não funciona no Firefox. Não testado no IE e no Edge.


Resultados para expressões de função

Resultado no NodeJS

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

Resultado no Chrome

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

Não funciona no Firefox, Opera. Não testado no IE e no Edge.

Notas:

  1. A função anônima não faz sentido verificar.
  2. Ambiente de teste

~ $ 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
fonte
1
(function f() {
    console.log(f.name);  //logs f
})();

Variação tipográfica:

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

f2(f1);  //Logs f1

Nota disponível apenas em mecanismos compatíveis com ES6 / ES2015. Para mais, veja

Ole
fonte
0

Aqui está um liner:

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

Como isso:

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

Essa é uma variante da resposta de Igor Ostroumov :

Se você deseja usá-lo como um valor padrão para um parâmetro, considere uma chamada de segundo nível para 'chamador':

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

Isso permitiria dinamicamente uma implementação reutilizável em várias funções.

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

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

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

function crow()
{
  bar();
}

foo();
crow();

Se você quiser o nome do arquivo também, aqui está essa solução usando a resposta do F-3000 em outra pergunta:

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
fonte
-1

Experimentar:

alert(arguments.callee.toString());
Deniz Dogan
fonte
3
Que voltaria a função inteira como uma string
Andy E
-7

A resposta é curta: alert(arguments.callee.name);

Manjericão
fonte
12
"nom" é "nome" em francês. Esse tipo de detalhe muda entre as versões de idioma dos navegadores? Eu não pensaria assim.
argyle