Como obter o nome da função de dentro dessa função?


263

Como posso acessar um nome de função de dentro dessa função?

// parasitic inheritance
var ns.parent.child = function() {
  var parent = new ns.parent();
  parent.newFunc = function() {

  }
  return parent;
}

var ns.parent = function() {
  // at this point, i want to know who the child is that called the parent
  // ie
}

var obj = new ns.parent.child();

bem, no pai, posso acessar outras funções por convenção, como ns [child] [schema] ou ns [child] [dbService]. Sem ele, eu tenho que codificar essas referências em todas as classes filho.
Scott Klarenbach

por que não passar a função filho como argumento para os pais? var pai = novo ns.parent (this);
plodder

porque existem dezenas dessas pesquisas e dezenas de crianças. Atualmente é o que estou fazendo, mas é sempre o mesmo e seria perfeito se essa lógica duplicada pudesse ser simplesmente colocada dentro do pai uma vez, com base na função derivada.
Scott Klarenbach

veja, não é a função filho que eu quero, é a convenção de nomenclatura usada, porque essa convenção de nomenclatura pode ser usada para carregar outras funções que não estão definidas atualmente no objeto filho, mas que estão relacionadas a essa criança em todo o sistema.
Scott Klarenbach

1
@ Scott Estou de acordo com todos os demais, pois você está gerenciando sua complexidade e estrutura de código incorretas para fazer isso. Esse tipo de acoplamento rígido é uma péssima decisão de design e vai fazer uma bagunça. @SimeVidas seu um grande necromante :)
Raynos

Respostas:


163

No ES5, a melhor coisa a fazer é:

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

O uso Function.callernão é padrão. Function.callere arguments.calleeambos são proibidos no modo estrito.

Edit: a resposta baseada em regex da nus abaixo consegue a mesma coisa, mas tem melhor desempenho!

No ES6, você pode apenas usar myFunction.name.

Nota: Lembre-se de que alguns minificadores JS podem jogar fora os nomes das funções, para compactar melhor; pode ser necessário ajustar as configurações para evitar isso.


Embora isso não tenha respondido à pergunta suficientemente para mim (devido a funções anônimas), ele me deu uma idéia para fazer isso: o fun.toString().substr(0, 100)que, para minhas necessidades, será suficiente para localizar a função em questão. Então, obrigado pela inspiração!
Campbeln

3
Sim, myFunction.nameé a melhor coisa a fazer no ES6.
Vlad A Ionescu

15
Qual é o sentido myFunction.namese você digitar o nome da função? derrota o propósito de variáveis ​​mágicas.
adi518

2
@ adi518: não necessariamente .. suponha que você tenha qualquer variedade de funções e você iteração sobre ele, usando for / foreach ..then arr[index].nametambém funcionará :)
Debasish Chowdhury

2
@ adi518 Não é inútil. Se eu estou renomeando a função no meu IDE, myFunction.nametambém será atualizado. Certamente, o intelliJ suporta renomear identificadores dentro de strings, mas isso funciona muito menos consistentemente e fica silenciosamente desatualizado se alguém esquecer de marcar a opção "search in strings". myFunction.nameé uma escolha um pouco melhor do que codificar a string.
byxor

141

ES6 (inspirado na resposta de sendy halim abaixo):

myFunction.name

Explanação no MDN . A partir de 2015, trabalha no nodejs e nos principais navegadores, exceto no IE.

Nota: Nas funções ligadas, isso dará " bound <originalName>". Você precisará remover o "limite" se desejar obter o nome original.


ES5 (inspirado na resposta de Vlad):

Se você tiver uma referência à função, poderá:

function functionName( func )
{
    // Match:
    // - ^          the beginning of the string
    // - function   the word 'function'
    // - \s+        at least some white space
    // - ([\w\$]+)  capture one or more valid JavaScript identifier characters
    // - \s*        optionally followed by white space (in theory there won't be any here,
    //              so if performance is an issue this can be omitted[1]
    // - \(         followed by an opening brace
    //
    var result = /^function\s+([\w\$]+)\s*\(/.exec( func.toString() )

    return  result  ?  result[ 1 ]  :  '' // for an anonymous function there won't be a match
}
  • Não executei testes de unidade nisso ou verifiquei diferenças de implementação, mas, em princípio, deve funcionar, se não deixar um comentário.
  • Nota: não funcionará em funções vinculadas
  • Nota: isso callere calleeé considerado obsoleto.

[1] Eu o incluo aqui porque é legal e muitas vezes as ferramentas de destaque de sintaxe falham em levar em consideração o espaço em branco entre o nome da função e os parênteses. Por outro lado, não conheço nenhuma implementação de .toString () que inclua espaço em branco aqui; é por isso que você pode omiti-lo.


Como resposta à pergunta original, eu descartaria a herança parasitária e buscaria alguns padrões de design de POO mais tradicionais. Eu escrevi um TidBits.OoJs para escrever confortavelmente código OOP em JavaScript com um conjunto de recursos que imitava C ++ (ainda não completo, mas principalmente).

Vejo pelos comentários que você gostaria de evitar a transmissão de informações parentnecessárias para seu construtor. Devo admitir que os padrões tradicionais de design não o salvam, pois geralmente é uma coisa boa tornar suas dependências óbvias e impostas.

Eu também sugeriria me afastar de funções anônimas. Eles apenas fazem da depuração e criação de perfil uma PITA porque tudo aparece como "função anônima", e não há nenhum benefício para eles que eu saiba.


3
Nice RegEx! Aqui está um teste de desempenho mostrando que seu código é o mais rápido em muitos casos. Em geral, parece que o RegEx bate o JS nativo em cerca de duas vezes a velocidade aqui, em média. jsperf.com/get-function-name/2 #
Beejor

@ Tomasz Oi, obrigado por apontar isso. Eu não sabia disso. Uma função vinculada é realmente uma nova função que envolve sua função original. A norma ecmascript § 19.2.3.2 define que o nome da nova função deve ser "bound" + originalName. O método toString também não funcionará em funções vinculadas ... Você terá que remover a palavra vinculada.

Qual é o sentido de myFunction.name se você digitar o nome da função? derrota o propósito de variáveis ​​mágicas.
Borjovsky 14/09/18

Observe que, se você estiver usando o React Native, isso pode não funcionar corretamente. Eu tenho um projeto de RN no qual estou trabalhando com a expo versão 36 e a .namepropriedade de um método do componente estava aparecendo como a string "value", independentemente do nome. Por incrível que pareça, durante os testes no aplicativo expo, tudo estava bem, mas uma vez compilado em um aplicativo real, ele travava silenciosamente.
Andy Corman

64

o que você está fazendo é atribuir uma função sem nome a uma variável. você provavelmente precisará da expressão da função nomeada ( http://kangax.github.com/nfe/ ).

var x = function x() {
    console.log( arguments.callee.name );
}
x();

no entanto, não tenho certeza de quanto é o navegador; há um problema no IE6 que faz com que você vaze o nome da função para o escopo externo. Além disso, o argumentos.callee é obsoleto e resultará em erro se você estiver usando strict mode.


1
Isso não funciona nas versões antigas do navegador Safari.
Anubhav Gupta

17

Qualquer constructorum expõe uma propriedade name, que é o nome da função. Você acessa o constructorvia uma instância (usando new) ou um prototype:

function Person() {
  console.log(this.constructor.name); //Person
}

var p = new Person();
console.log(p.constructor.name); //Person

console.log(Person.prototype.constructor.name);  //Person

6
A primeira console.logchamada registra windowou Objectpara mim, dependendo de onde eu a corro, não Person.
24516 Bart Bart

Tem certeza de que instanciou usando "novo"? Caso contrário, não funcionará.
Roland

14

Parece a coisa mais estúpida que escrevi na minha vida, mas é engraçado: D

function getName(d){
  const error = new Error();
  const firefoxMatch = (error.stack.split('\n')[0 + d].match(/^.*(?=@)/) || [])[0];
  const chromeMatch = ((((error.stack.split('at ') || [])[1 + d] || '').match(/(^|\.| <| )(.*[^(<])( \()/) || [])[2] || '').split('.').pop();
  const safariMatch = error.stack.split('\n')[0 + d];

  // firefoxMatch ? console.log('firefoxMatch', firefoxMatch) : void 0;
  // chromeMatch ? console.log('chromeMatch', chromeMatch) : void 0;
  // safariMatch ? console.log('safariMatch', safariMatch) : void 0;

  return firefoxMatch || chromeMatch || safariMatch;
}

d- profundidade da pilha. 0- retorna este nome de função, 1- pai, etc .;
[0 + d]- apenas para entender - o que acontece;
firefoxMatch- trabalha para o safari, mas eu realmente tive um pouco de tempo para testar, porque o dono do mac voltou depois de fumar e me afastou: '(

Teste:

function limbo(){
  for(let i = 0; i < 4; i++){
    console.log(getName(i));
  }
}
function lust(){
  limbo();
}
function gluttony(){
  lust();
}

gluttony();

Resultado:
Chrome:
insira a descrição da imagem aqui

Fitefox:
insira a descrição da imagem aqui

Esta solução foi criada apenas por diversão ! Não o use para projetos reais. Não depende da especificação do ES, depende apenas da realização do navegador. Após a próxima atualização do chrome / firefox / safari, ele pode estar quebrado.
Mais do que isso, não há erro (ha) no processamento - se dfor maior que o comprimento da pilha - você receberá um erro;
Para o padrão de mensagem de erro de outros wrowsers - você receberá um erro;
Ele deve funcionar para as classes ES6 ( .split('.').pop()), mas você pode obter um erro;


13

Isso pode funcionar para você:

function foo() { bar(); }

function bar() { console.log(bar.caller.name); }

executar foo () produzirá "foo" ou indefinido se você chamar de uma função anônima.

Também funciona com construtores; nesse caso, seria exibido o nome do construtor que chamava (por exemplo, "Foo").

Mais informações aqui: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller

Eles alegam que não é padrão, mas também é suportado por todos os principais navegadores: Firefox, Safari, Chrome, Opera e IE.


Não disponível no modo estrito ... mmm
Ka Mok

8

Você não pode. As funções não têm nomes de acordo com o padrão (embora o mozilla possua esse atributo) - elas podem ser atribuídas apenas a variáveis ​​com nomes.

Também o seu comentário:

// access fully qualified name (ie "my.namespace.myFunc")

está dentro da função my.namespace.myFunc.getFn

O que você pode fazer é retornar o construtor de um objeto criado por novos

Então você poderia dizer

var obj = new my.namespace.myFunc();
console.info(obj.constructor); //my.namespace.myFunc

1
Eu preciso disso dentro do func porque estou passando pela cadeia de herança e omiti esse material por questões de brevidade.
Scott Klarenbach

1
'this' será a instância que invocou a função. Portanto, se você 'isso' dentro da função pai, isso fornecerá a instância da função que a chamou. É tudo o que deve precisar - não tenho certeza por isso que você precisa do nome também
plodder

1
bem, no pai, posso acessar outras funções por convenção, como ns [child] [schema] ou ns [child] [dbService]. Sem ele, eu tenho que codificar essas referências em todas as classes filho.
Scott Klarenbach

1
meu caso de uso para localizar o nome é facilitar a construção de mensagens console.error que informam de onde a mensagem veio sem ter que reverter para o despejo de pilha. por exemplo console.error ({'error': {self.name reference} + "erro com o parâmetro de entrada"). Torna-se muito mais fácil cortar / colar mensagens de erro que se repetem em várias funções se você não precisar parar e alterar o texto dentro delas a cada vez. No meu caso, estou retornando erros de promessa de várias chamadas de promessa - é bom saber qual promessa / função gerou o erro.
Scott Scott

7

Você pode usar isso para navegadores que suportam Error.stack (não quase todos, provavelmente)

function WriteSomeShitOut(){ 
  var a = new Error().stack.match(/at (.*?) /);
  console.log(a[1]);
} 
WriteSomeShitOut();

é claro que isso é para a função atual, mas você entendeu.

babando feliz enquanto você codifica


4

Você pode usar a namepropriedade para obter o nome da função, a menos que esteja usando uma função anônima

Por exemplo:

var Person = function Person () {
  this.someMethod = function () {};
};

Person.prototype.getSomeMethodName = function () {
  return this.someMethod.name;
};

var p = new Person();
// will return "", because someMethod is assigned with anonymous function
console.log(p.getSomeMethodName());

agora vamos tentar com a função nomeada

var Person = function Person () {
  this.someMethod = function someMethod() {};
};

agora você pode usar

// will return "someMethod"
p.getSomeMethodName()

4

Você pode usar o nome do construtor como:

{your_function}.prototype.constructor.name

esse código simplesmente retorna o nome de um método.


3

como parte como ECMAScript 6você pode usar o método Function.name

function doSomething() {}

alert(doSomething.name); // alerts "doSomething"

No ReactJS, você precisa adicionar isso: console.log (this.doSomething.name);
Bitclaw

2
que é fora da função
Scott

3

Você poderia usarFunction.name :

Na maioria das implementações de JavaScript, depois de ter a referência do construtor no escopo, você pode obter o nome da string a partir da propriedade name (por exemplo, Function.name ou Object.constructor.name

Você poderia usarFunction.callee :

O arguments.callermétodo nativo foi descontinuado, mas a maioria dos navegadores suporta Function.caller, que retornará o objeto de chamada real (seu corpo de código): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ Função / chamador? Redirectlocale = pt-BR & redirectslug = JavaScript% 2FReference% 2FGlobal_Objects% 2FFunction% 2Fcaller

Você pode criar um mapa de origem :

Se o que você precisa é a assinatura da função literal (o "nome" dela) e não o objeto em si, talvez seja necessário recorrer a algo um pouco mais personalizado, como criar uma referência de matriz dos valores de string da API que você precisará acesse com freqüência. Você pode mapeá-los juntos usando Object.keys()sua matriz de strings, ou procurar na biblioteca de mapas de fontes do Mozilla no GitHub, para projetos maiores: https://github.com/mozilla/source-map


2

Sei que essa é uma pergunta antiga, mas ultimamente tenho enfrentado um problema semelhante ao tentar decorar alguns métodos do React Component, para fins de depuração. Como as pessoas já disseram, arguments.callere arguments.calleesão proibidas no modo estrito, que provavelmente está ativado por padrão na transpilação do React. Você pode desativá-lo ou eu pude criar outro hack, porque em React todas as funções de classe são nomeadas, você pode realmente fazer isso:

Component.prototype.componentWillMount = function componentWillMount() {
    console.log('Callee name: ', this.__proto__.constructor.toString().substr(0,30));
...
}

1

Isso funcionou para mim.

function AbstractDomainClass() {
    this.className = function() {
        if (!this.$className) {
            var className = this.constructor.toString();
            className = className.substr('function '.length);
            className = className.substr(0, className.indexOf('('));
            this.$className = className;
        }
        return this.$className;
    }
}

Código do teste:

var obj = new AbstractDomainClass();
expect(obj.className()).toBe('AbstractDomainClass');

1

Eu tive um problema semelhante e resolvi-o da seguinte maneira:

Function.prototype.myname = function() {
   return this.toString()
       .substr( 0, this.toString().indexOf( "(" ) )
       .replace( "function ", "" ); 
}

Esse código implementa, de uma maneira mais confortável, uma resposta que eu já li aqui no início desta discussão. Agora eu tenho uma função de membro recuperando o nome de qualquer objeto de função. Aqui está o script completo ...

<script language="javascript" TYPE="text/javascript">

    Function.prototype.myname = function() { 
        return this.toString()
            .substr( 0, this.toString().indexOf( "(" ) )
            .replace("function ", "" ); 
    }
    function call_this( _fn ) { document.write( _fn.myname() ); }
    function _yeaaahhh() { /* do something */ }
    call_this( _yeaaahhh ); 

</script>


0

Isso funcionará no ES5, ES6, em todos os navegadores e funções de modo estrito.

Veja como fica com uma função nomeada.

(function myName() {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at myName (<anonymous>:2:15)

Veja como fica com uma função anônima.

(() => {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at <anonymous>:2:15

Isso produz resultados diferentes por navegador. Chrome: at myName (<anonymous>:2:15)Firefox:@debugger eval code:3:3
jkdev

(função myName () {console.log (novo erro (). stack.split (/ \ r \ n | \ r | \ n / g) [1] .trim (). split ("") [1]) ;}) ();
Zibri 01/02


-2

você pode usar o Error.stack para rastrear o nome da função e a posição exata de onde você está.

Veja stacktrace.js


-3

Maneira fácil de obter o nome da função de dentro da função que você está executando.

function x(){alert(this.name)};x()


1
me dá um GUID
Tamir Daniely

1
estar ciente de que o conteúdo thisbasicamente pode ser qualquer coisa. tente(function(){console.log(this.name);}).bind({name: "put basically anything here even an object maybe a 1TB string maybe a class definition"})()
Valen
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.