Come ottenere il nome della funzione all'interno di quella funzione?


263

Come posso accedere al nome di una funzione dall'interno di quella funzione?

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

bene, nel genitore, posso quindi accedere ad altre funzioni per convenzione, come ns [child] [schema] o ns [child] [dbService]. Senza di essa, devo codificare questi riferimenti in ogni classe figlio.
Scott Klarenbach,

perché non passare semplicemente la funzione figlio come argomento al genitore? var parent = new ns.parent (this);
plodder,

perché ci sono dozzine di tali ricerche e dozzine di bambini. Questo è attualmente quello che sto facendo, ma è sempre lo stesso e sarebbe perfetto se quella logica duplicata potesse essere semplicemente collocata all'interno del genitore una volta, in base alla funzione derivata.
Scott Klarenbach,

vedi, non è la funzione figlio che voglio, è la convenzione di denominazione usata, perché quella convenzione di denominazione può essere usata per caricare altre funzioni che non sono attualmente definite sull'oggetto figlio, ma sono correlate a quella figlia in tutto il sistema.
Scott Klarenbach,

1
@Scott Sono d'accordo con tutti gli altri, la tua gestione della complessità e la struttura del codice sono sbagliate per aver bisogno di farlo. Questo tipo di accoppiamento duro è una cattiva decisione di progettazione e farà un casino. @SimeVidas sei un grande negromante :)
Raynos,

Risposte:


163

In ES5, la cosa migliore da fare è:

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

L'uso Function.callernon è standard. Function.callere arguments.calleesono entrambi vietati in modalità rigorosa.

Modifica: la risposta basata su regex di nus qui sotto raggiunge la stessa cosa, ma ha prestazioni migliori!

In ES6, puoi semplicemente usare myFunction.name.

Nota: attenzione che alcuni minificatori JS potrebbero eliminare i nomi delle funzioni per comprimere meglio; potrebbe essere necessario modificarne le impostazioni per evitarlo.


Sebbene questo non abbia risposto sufficientemente alla domanda per me (a causa di funzioni anonime), mi ha dato l'idea di farlo: fun.toString().substr(0, 100)quale per le mie esigenze sarà sufficiente per individuare la funzione in questione. Quindi, grazie per l'ispirazione!
Campbeln,

3
Sì, myFunction.nameè la cosa migliore da fare in ES6.
Vlad A Ionescu,

15
Qual è il punto myFunction.namese digiti il ​​nome della funzione? sconfigge lo scopo delle variabili magiche.
adi518,

2
@ adi518: non necessariamente .. supponi di avere una serie di funzioni e di iterare su di essa, usando for / foreach .. quindi arr[index].namefunzionerà anche :)
Debasish Chowdhury

2
@ adi518 Non è inutile. Se sto rinominando la funzione all'interno del mio IDE, myFunction.nameverrà anche aggiornato. Certo, IntelliJ supporta la ridenominazione degli identificatori all'interno delle stringhe, ma funziona in modo molto meno coerente e diventa silenziosamente obsoleto se qualcuno dimentica di selezionare l'opzione "Cerca nelle stringhe". myFunction.nameè una scelta leggermente migliore rispetto all'hardcoding della stringa.
byxor,

141

ES6 (ispirato dalla risposta di Halim Sendy di seguito):

myFunction.name

Spiegazione su MDN . A partire dal 2015 funziona in nodejs e tutti i principali browser tranne IE.

Nota: sulle funzioni associate questo darà " bound <originalName>". Dovrai eliminare il "limite" se vuoi ottenere il nome originale.


ES5 (ispirato alla risposta di Vlad):

Se hai un riferimento alla funzione, puoi fare:

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
}
  • Non ho eseguito test unitari su questo, o verificato differenze di implementazione, ma in linea di principio dovrebbe funzionare, se non lasciare un commento.
  • Nota: non funziona con le funzioni associate
  • Nota: quello callere calleesono considerati deprecati.

[1] Lo includo qui perché è legale e spesso abbastanza strumenti di evidenziazione della sintassi non tengono conto dello spazio bianco tra il nome della funzione e la parentesi. D'altra parte, non sono a conoscenza di alcuna implementazione di .toString () che includerà spazi bianchi qui, quindi è per questo che puoi ometterlo.


Come risposta alla domanda originale, lascerei cadere l'eredità parassitaria e scegliere alcuni modelli di design OOP più tradizionali. Ho scritto un TidBits.OoJs per scrivere comodamente il codice OOP in JavaScript con un set di funzionalità che imita il C ++ (non ancora completo, ma soprattutto).

Vedo dai commenti che vorresti evitare di passare le informazioni parentnecessarie al suo costruttore. Devo ammettere che i modelli di design tradizionali non ti salveranno da quello, dal momento che è generalmente una buona cosa rendere ovvie e applicate le tue dipendenze.

Vorrei anche suggerire di evitare le funzioni anonime. Fanno solo il debug e la profilazione di un PITA perché tutto si presenta come una "funzione anonima", e non ho alcun vantaggio di cui io sia a conoscenza.


3
Nice RegEx! Ecco un test delle prestazioni che mostra che il tuo codice è il più veloce in molti casi. In generale sembra che RegEx batte JS nativo di circa 2x velocità qui in media. jsperf.com/get-function-name/2
Beejor,

@Tomasz Ciao, grazie per averlo sottolineato. Non lo sapevo. Una funzione associata è in realtà una nuova funzione che avvolge la funzione originale. Lo standard ecmascript § 19.2.3.2 definisce che il nome della nuova funzione deve essere "associato" + nome originale. Il metodo toString non funzionerà nemmeno sulle funzioni associate ... Dovrai eliminare la parola associata.

Qual è il punto in myFunction.name se si digita il nome della funzione? sconfigge lo scopo delle variabili magiche.
Borjovsky,

Nota che se stai utilizzando React Native potrebbe non funzionare correttamente. Ho un progetto RN su cui sto lavorando con la versione 36 di expo e la .nameproprietà di un metodo componente si presentava come la stringa "valore", indipendentemente da come si chiamava. Stranamente, durante il test nell'app expo andava bene, tuttavia, una volta compilato in una vera applicazione, si sarebbe bloccato in modo silenzioso.
Andy Corman,

64

quello che stai facendo è assegnare una funzione senza nome a una variabile. probabilmente è invece necessaria l'espressione della funzione denominata ( http://kangax.github.com/nfe/ ).

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

tuttavia non sono sicuro di quanto sia cross-browser; c'è un problema con IE6 che ti fa perdere il nome della funzione nell'ambito esterno. inoltre, argomenti.callee è un po 'deprecato e comporterà un errore se si utilizza strict mode.


1
Questo non funziona con le vecchie versioni del browser Safari.
Anubhav Gupta,

17

Qualsiasi constructorespone una proprietàname , che è il nome della funzione. Si accede constructortramite un'istanza (utilizzando new) o un 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
La prima console.logchiamata registra windowo Objectper me a seconda di dove la eseguo, non Person.
Bart S,

Sei sicuro di aver istanziato usando "nuovo"? Altrimenti non può funzionare.
roland,

14

Sembra la cosa più stupida, che ho scritto nella mia vita, ma è divertente: 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- profondità di pila. 0- restituisce questo nome di funzione, 1- genitore, ecc .;
[0 + d]- solo per capire - cosa succede;
firefoxMatch- funziona per il safari, ma ho avuto davvero poco tempo per i test, perché il proprietario di Mac era tornato dopo aver fumato e mi ha portato via: "(

test:

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

gluttony();

Risultato:
Chrome:
inserisci qui la descrizione dell'immagine

Fitefox:
inserisci qui la descrizione dell'immagine

Questa soluzione è stata creata solo per divertimento ! Non usarlo per progetti reali. Non dipende dalle specifiche ES, dipende solo dalla realizzazione del browser. Dopo il prossimo aggiornamento di Chrome / Firefox / Safari potrebbe essere rotto.
Oltre a ciò non vi è alcun errore (ha) elaborazione - se dsarà più della lunghezza dello stack - si otterrà un errore;
Per il modello di messa in errore di altri wrowser - si otterrà un errore;
Deve funzionare per le classi ES6 ( .split('.').pop()), ma puoi ottenere un errore;


13

Questo potrebbe funzionare per te:

function foo() { bar(); }

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

l'esecuzione di foo () produrrà "foo" o indefinito se si chiama da una funzione anonima.

Funziona anche con i costruttori, nel qual caso emetterebbe il nome del costruttore chiamante (ad esempio "Foo").

Maggiori informazioni qui: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller

Sostengono che non è standard, ma è anche supportato da tutti i principali browser: Firefox, Safari, Chrome, Opera e IE.


Non disponibile in modalità rigorosa ... mmm
Ka Mok il

8

Non puoi. Le funzioni non hanno nomi in base allo standard (sebbene mozilla abbia un tale attributo) - possono essere assegnate solo a variabili con nomi.

Anche il tuo commento:

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

è all'interno della funzione my.namespace.myFunc.getFn

Quello che puoi fare è restituire il costruttore di un oggetto creato da new

Quindi potresti dire

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

1
ne ho bisogno all'interno della funzione perché sto passando la catena dell'eredità e ho omesso quella roba per brevità.
Scott Klarenbach,

1
'this' sarà l'istanza che ha invocato la funzione. Quindi se 'questo' all'interno della funzione genitore ti darà l'istanza della funzione che l'ha chiamata. Questo è tutto ciò di cui hai bisogno - non so perché anche tu hai bisogno del nome
plodder

1
bene, nel genitore, posso quindi accedere ad altre funzioni per convenzione, come ns [child] [schema] o ns [child] [dbService]. Senza di essa, devo codificare questi riferimenti in ogni classe figlio.
Scott Klarenbach,

1
il mio caso d'uso per trovare il nome è di semplificare la costruzione di messaggi console.error che indicano da dove proviene il messaggio senza dover tornare al dump dello stack. ad es. console.error ({'error': {riferimento self.name} + "errore con parametro di input"). Diventa molto più facile tagliare / incollare i messaggi di errore che si ripetono in più funzioni se non devi fermarti e cambiare il testo al loro interno ogni volta. Nel mio caso, sto restituendo errori di promessa da più chiamate di promessa - bello sapere quale promessa / funzione ha generato l'errore.
Scott,

7

Puoi usarlo, per i browser che supportano Error.stack (non quasi tutti, probabilmente)

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

ovviamente questo è per la funzione corrente, ma hai l'idea.

felice sbavando mentre codice


4

Puoi usare name proprietà per ottenere il nome della funzione, a meno che non si stia utilizzando una funzione anonima

Per esempio:

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

ora proviamo con la funzione denominata

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

ora puoi usare

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

4

Puoi usare il nome del costruttore come:

{your_function}.prototype.constructor.name

questo codice restituisce semplicemente il nome di un metodo.


3

come parte ECMAScript 6è possibile utilizzare il metodo Function.name

function doSomething() {}

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

In ReactJS devi aggiungere questo: console.log (this.doSomething.name);
Bitclaw,

2
che è al di fuori della funzione
Scott,

3

Puoi usareFunction.name :

Nella maggior parte delle implementazioni di JavaScript, una volta ottenuto il riferimento del costruttore nell'ambito, è possibile ottenere il nome della stringa dalla proprietà name (ad es. Function.name o Object.constructor.name

Puoi usareFunction.callee :

Il arguments.callermetodo nativo è stato deprecato, ma la maggior parte dei browser supporta Function.caller, che restituirà l'oggetto invocante effettivo (il suo corpo di codice): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ funzione / chiamante? redirectlocale = it-IT & redirectslug = JavaScript% 2FReference% 2FGlobal_Objects% 2FFunction% 2Fcaller

È possibile creare una mappa di origine :

Se ciò di cui hai bisogno è la firma della funzione letterale (il "nome" di esso) e non l'oggetto stesso, potresti dover ricorrere a qualcosa di un po 'più personalizzato, come la creazione di un riferimento di matrice dei valori della stringa API che dovrai accedere frequentemente. Puoi mapparli insieme usando Object.keys()e il tuo array di stringhe, oppure guardare nella libreria di mappe sorgente di Mozilla su GitHub, per progetti più grandi: https://github.com/mozilla/source-map


2

So che questa è una vecchia domanda, ma ultimamente ho riscontrato un problema simile mentre cercavo di decorare alcuni metodi di React Component, a scopo di debug. Dato che le persone già detto, arguments.callere arguments.calleesono vietati in modalità rigorosa che probabilmente è attivata per impostazione predefinita nel vostro Reagire transpiling. Puoi disabilitarlo o sono stato in grado di inventare un altro hack, perché in React tutte le funzioni di classe sono nominate, puoi effettivamente farlo:

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

1

Questo ha funzionato per me.

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

Codice di prova:

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

1

Ho avuto un problema simile e l'ho risolto come segue:

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

Questo codice implementa, in modo più comodo, una risposta che ho già letto qui all'inizio di questa discussione. Ora ho una funzione membro che recupera il nome di qualsiasi oggetto funzione. Ecco lo 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>

1

Se ho capito cosa volevi fare, questo è quello che faccio all'interno di un costruttore di funzioni.

if (!(this instanceof arguments.callee)) {
    throw "ReferenceError: " + arguments.callee.name + " is not defined";
}

2
Nota:
obsoleto

0

Funzionerà con ES5, ES6, tutti i browser e le funzioni in modalità rigorosa.

Ecco come appare con una funzione denominata.

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

Ecco come appare con una funzione anonima.

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

Ciò produce risultati diversi per browser. Chrome: at myName (<anonymous>:2:15)Firefox:@debugger eval code:3:3
jkdev il

(function myName () {console.log (new Error (). stack.split (/ \ r \ n | \ r | \ n / g) [1] .trim (). split ("") [1]) ;}) ();
Zibri


-2

puoi usare Error.stack per tracciare il nome della funzione e la posizione esatta di dove ti trovi.

Vedi stacktrace.js


-3

Un modo semplice per ottenere il nome della funzione all'interno di fuction in esecuzione.

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


1
mi dà un GUID
Tamir Daniely il

1
essere consapevoli del contenuto di thisesso in sostanza può essere qualsiasi cosa. prova(function(){console.log(this.name);}).bind({name: "put basically anything here even an object maybe a 1TB string maybe a class definition"})()
Valen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.