Wie erhalte ich den Funktionsnamen aus dieser Funktion heraus?

263

Wie kann ich innerhalb dieser Funktion auf einen Funktionsnamen zugreifen?

// 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();
Scott Klarenbach
quelle
Nun, im übergeordneten Element kann ich dann gemäß Konvention auf andere Funktionen zugreifen, wie z. B. ns [Kind] [Schema] oder ns [Kind] [dbService]. Ohne sie muss ich diese Referenzen in jeder Kinderklasse hart codieren.
Scott Klarenbach
Warum nicht einfach die untergeordnete Funktion als Argument an das übergeordnete Element übergeben? var parent = new ns.parent (this);
Plodder
weil es Dutzende solcher Suchanfragen und Dutzende von Kindern gibt. Das ist momentan das, was ich mache, aber es ist jedes Mal das gleiche und wäre perfekt, wenn diese doppelte Logik basierend auf der abgeleiteten Funktion einfach einmal im übergeordneten Element platziert werden könnte.
Scott Klarenbach
Sehen Sie, es ist nicht die untergeordnete Funktion, die ich möchte, sondern die verwendete Namenskonvention, da diese Namenskonvention verwendet werden kann, um andere Funktionen zu laden, die derzeit nicht für das untergeordnete Objekt definiert sind, sich jedoch systemweit auf dieses untergeordnete Objekt beziehen.
Scott Klarenbach
1
@Scott Ich stimme allen anderen zu, dass Sie Ihre Komplexität und Codestruktur falsch verwalten, um dies tun zu müssen. Diese Art der harten Kopplung ist eine schlechte Designentscheidung und wird ein Chaos verursachen. @SimeVidas du bist ein großartiger Nekromant :)
Raynos

Antworten:

163

In ES5 ist das Beste:

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

Die Verwendung Function.callerist nicht Standard. Function.callerund arguments.calleesind beide im strengen Modus verboten.

Bearbeiten: Die unten auf Regex basierende Antwort von nus erreicht dasselbe, hat aber eine bessere Leistung!

In ES6 können Sie nur verwenden myFunction.name.

Hinweis: Beachten Sie, dass einige JS-Minifizierer möglicherweise Funktionsnamen wegwerfen, um eine bessere Komprimierung zu erzielen. Möglicherweise müssen Sie ihre Einstellungen anpassen, um dies zu vermeiden.

Vlad A Ionescu
quelle
Dies beantwortete die Frage zwar nicht ausreichend für mich (aufgrund anonymer Funktionen), gab mir jedoch die Idee, dies zu tun: fun.toString().substr(0, 100)Was für meine Bedürfnisse ausreicht, um die betreffende Funktion zu lokalisieren. Also, danke für die Inspiration!
Campbeln
3
Yup myFunction.nameist das Beste, was man in ES6 machen kann.
Vlad A Ionescu
15
Was bringt es, myFunction.namewenn Sie den Funktionsnamen eingeben? besiegt den Zweck von magischen Variablen.
adi518
2
@ adi518: nicht unbedingt .. Angenommen, Sie haben eine Reihe von Funktionen und Sie iterieren darüber, indem Sie for / foreach verwenden .. dann arr[index].namewird auch funktionieren :)
Debasish Chowdhury
2
@ adi518 Es ist nicht nutzlos. Wenn ich die Funktion in meiner IDE umbenenne, myFunction.namewird sie ebenfalls aktualisiert. Sicher, IntelliJ unterstützt das Umbenennen von Bezeichnern innerhalb von Zeichenfolgen, aber das funktioniert viel weniger konsistent und wird stillschweigend veraltet, wenn jemand vergisst, die Option "In Zeichenfolgen suchen" anzukreuzen. myFunction.nameist eine etwas bessere Wahl als die Hardcodierung der Zeichenfolge.
Bis zum
141

ES6 (inspiriert von der Antwort von sendy halim unten):

myFunction.name

Erläuterung zu MDN . Ab 2015 funktioniert in NodeJS und allen gängigen Browsern außer IE.

Hinweis: Bei gebundenen Funktionen ergibt dies " bound <originalName>". Sie müssen die "Bindung" entfernen, wenn Sie den ursprünglichen Namen erhalten möchten.


ES5 (inspiriert von Vlads Antwort):

Wenn Sie einen Verweis auf die Funktion haben, können Sie Folgendes tun:

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
}
  • Ich habe hierzu keine Komponententests durchgeführt oder Implementierungsunterschiede überprüft, aber im Prinzip sollte es funktionieren, wenn ich keinen Kommentar hinterlasse.
  • Hinweis: Funktioniert nicht mit gebundenen Funktionen
  • Hinweis: dass callerund calleegelten als veraltet.

[1] Ich füge es hier ein, weil es legal ist und häufig genug Syntaxhervorhebungswerkzeuge den Leerraum zwischen Funktionsname und Klammer nicht berücksichtigen. Andererseits ist mir keine Implementierung von .toString () bekannt, die hier Leerzeichen enthält. Deshalb können Sie sie weglassen.


Als Antwort auf die ursprüngliche Frage würde ich die parasitäre Vererbung fallen lassen und mich für einige traditionellere OOP-Entwurfsmuster entscheiden. Ich habe ein TidBits.OoJs geschrieben, um bequem OOP-Code in JavaScript mit einem Funktionssatz zu schreiben, der C ++ nachahmt (noch nicht vollständig, aber meistens).

Ich sehe aus den Kommentaren, dass Sie vermeiden möchten, dass Informationsbedürfnisse parentan den Konstruktor weitergegeben werden. Ich muss zugeben, dass traditionelle Entwurfsmuster Sie jedoch nicht davor bewahren werden, da es im Allgemeinen als eine gute Sache angesehen wird, Ihre Abhängigkeiten offensichtlich zu machen und durchzusetzen.

Ich würde auch vorschlagen, sich von anonymen Funktionen fernzuhalten. Sie machen das Debuggen und Profilieren einer PITA nur, weil alles nur als "anonyme Funktion" angezeigt wird und es keinen Nutzen für sie gibt, den ich kenne.


quelle
3
Schöne RegEx! Hier ist ein Leistungstest, der zeigt, dass Ihr Code in vielen Fällen der schnellste ist. Im Allgemeinen scheint RegEx das native JS hier im Durchschnitt um etwa das Zweifache zu übertreffen. jsperf.com/get-function-name/2
Beejor
@Tomasz Hallo, danke, dass du darauf hingewiesen hast. Das wusste ich nicht. Eine gebundene Funktion ist eigentlich eine neue Funktion, die Ihre ursprüngliche Funktion umschließt. Der Ecmascript-Standard § 19.2.3.2 definiert, dass der Name der neuen Funktion "gebunden" + originalName sein soll. Die toString-Methode funktioniert auch nicht mit gebundenen Funktionen ... Sie müssen das gebundene Wort entfernen.
Was bringt myFunction.name, wenn Sie den Funktionsnamen eingeben? besiegt den Zweck von magischen Variablen.
Borjovsky
Beachten Sie, dass dies bei Verwendung von React Native möglicherweise nicht ordnungsgemäß funktioniert. Ich habe ein RN-Projekt, an dem ich mit der Expo-Version 36 arbeite, und die .nameEigenschaft einer Komponentenmethode wurde als Zeichenfolge "Wert" angezeigt, unabhängig davon, wie sie aufgerufen wurde. Seltsamerweise war es beim Testen in der Expo-App in Ordnung, aber sobald es zu einer echten Anwendung kompiliert wurde, stürzte es lautlos ab.
Andy Corman
64

Sie weisen einer Variablen eine unbenannte Funktion zu. Sie benötigen wahrscheinlich stattdessen einen benannten Funktionsausdruck ( http://kangax.github.com/nfe/ ).

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

Ich bin mir jedoch nicht sicher, wie viel browserübergreifend das ist. Es gibt ein Problem mit IE6, bei dem der Name der Funktion in den äußeren Bereich gelangt. Außerdem ist argument.callee veraltet und führt zu Fehlern, wenn Sie verwenden strict mode.

Platzhalter
quelle
1
Dies funktioniert nicht mit alten Versionen des Safari-Browsers.
Anubhav Gupta
17

Any constructormacht eine Eigenschaft verfügbar name, bei der es sich um den Funktionsnamen handelt. Sie greifen constructorüber eine Instanz (using new) oder eine 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
Roland
quelle
6
Der erste console.logAnruf protokolliert windowoder Objectfür mich je nachdem, wo ich ihn ausführe, nicht Person.
Bart S
Sind Sie sicher, dass Sie mit "new" instanziiert haben? Sonst kann es nicht funktionieren.
roland
13

Dies könnte für Sie funktionieren:

function foo() { bar(); }

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

Wenn Sie foo () ausführen, wird "foo" oder undefiniert ausgegeben, wenn Sie von einer anonymen Funktion aus aufrufen.

Es funktioniert auch mit Konstruktoren. In diesem Fall wird der Name des aufrufenden Konstruktors ausgegeben (z. B. "Foo").

Weitere Informationen finden Sie hier: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller

Sie behaupten, es sei nicht Standard, aber es werde auch von allen gängigen Browsern unterstützt: Firefox, Safari, Chrome, Opera und IE.

Jacob Mouka
quelle
Nicht verfügbar im strengen Modus ... mmm
Ka Mok
13

Es sieht aus wie das Dümmste, was ich in meinem Leben geschrieben habe, aber es ist lustig: 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- Stapeltiefe. 0- diesen Funktionsnamen zurückgeben, 1- Eltern usw.;
[0 + d]- nur zum Verständnis - was passiert;
firefoxMatch- arbeitet für eine Safari, aber ich hatte wirklich ein wenig Zeit zum Testen, weil Macs Besitzer nach dem Rauchen zurückgekehrt war und mich weggefahren hat: '(

Testen:

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

gluttony();

Ergebnis:
Chrome:
Geben Sie hier die Bildbeschreibung ein

Fitefox:
Geben Sie hier die Bildbeschreibung ein

Diese Lösung wurde nur zum Spaß erstellt ! Verwenden Sie es nicht für echte Projekte. Es hängt nicht von der ES-Spezifikation ab, sondern nur von der Browser-Realisierung. Nach dem nächsten Chrome / Firefox / Safari-Update ist es möglicherweise defekt.
Darüber hinaus gibt es keine Fehlerverarbeitung (ha). Wenn ddie Stapellänge größer als die Stapellänge ist, wird ein Fehler angezeigt.
Für das Messaage-Muster anderer Wrowser-Fehler wird ein Fehler angezeigt.
Es muss für ES6-Klassen ( .split('.').pop()) funktionieren , aber Sie können immer noch einen Fehler bekommen;

user3335966
quelle
8

Das kannst du nicht. Funktionen haben keine standardmäßigen Namen (obwohl Mozilla ein solches Attribut hat) - sie können nur Variablen mit Namen zugewiesen werden.

Auch dein Kommentar:

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

befindet sich in der Funktion my.namespace.myFunc.getFn

Sie können den Konstruktor eines von new erstellten Objekts zurückgeben

Man könnte also sagen

var obj = new my.namespace.myFunc();
console.info(obj.constructor); //my.namespace.myFunc
Arbeitstier
quelle
1
Ich brauche es in der Funk, weil ich es in der Vererbungskette weitergebe und das Zeug der Kürze halber weggelassen habe.
Scott Klarenbach
1
'this' ist die Instanz, die die Funktion aufgerufen hat. Wenn Sie also 'dies' in der übergeordneten Funktion eingeben, erhalten Sie die Instanz der Funktion, die sie aufgerufen hat. Das ist alles, was Sie brauchen sollten - nicht sicher, warum Sie den Namen auch brauchen
plodder
1
Nun, im übergeordneten Element kann ich dann gemäß Konvention auf andere Funktionen zugreifen, wie z. B. ns [Kind] [Schema] oder ns [Kind] [dbService]. Ohne sie muss ich diese Referenzen in jeder Kinderklasse hart codieren.
Scott Klarenbach
1
Mein Anwendungsfall für das Auffinden des Namens besteht darin, das Erstellen von console.error-Nachrichten zu vereinfachen, die angeben, woher die Nachricht stammt, ohne zum Stack-Dump zurückkehren zu müssen. zB console.error ({'error': {self.name reference} + "Fehler mit Eingabeparameter"). Es wird viel einfacher, Fehlermeldungen auszuschneiden / einzufügen, die sich in mehreren Funktionen wiederholen, wenn Sie nicht jedes Mal anhalten und den darin enthaltenen Text ändern müssen. In meinem Fall gebe ich Versprechensfehler aus mehreren Versprechungsaufrufen zurück - schön zu wissen, welches Versprechen / welche Funktion den Fehler erzeugt hat.
Scott
7

Sie können dies für Browser verwenden, die Error.stack unterstützen (wahrscheinlich nicht fast alle).

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

Dies gilt natürlich für die aktuelle Funktion, aber Sie haben die Idee.

glücklich sabbern, während Sie codieren

no_ripcord
quelle
4

Sie können die nameEigenschaft verwenden, um den Funktionsnamen abzurufen, es sei denn, Sie verwenden eine anonyme Funktion

Beispielsweise:

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

Versuchen wir es jetzt mit der benannten Funktion

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

Jetzt können Sie verwenden

// will return "someMethod"
p.getSomeMethodName()
sendy halim
quelle
4

Sie können den Konstruktornamen wie folgt verwenden:

{your_function}.prototype.constructor.name

Dieser Code gibt einfach den Namen einer Methode zurück.

Muhamad Zolfaghari
quelle
3

als Teil, wie ECMAScript 6Sie die Function.name- Methode verwenden können

function doSomething() {}

alert(doSomething.name); // alerts "doSomething"
Pleerock
quelle
In ReactJS müssen Sie Folgendes hinzufügen: console.log (this.doSomething.name);
Bitclaw
2
das ist außerhalb der Funktion
Scott
3

Sie könnten verwendenFunction.name :

In den meisten Implementierungen von JavaScript können Sie, sobald Sie die Referenz Ihres Konstruktors im Gültigkeitsbereich haben, den Zeichenfolgennamen aus der Namenseigenschaft abrufen (z. B. Function.name oder Object.constructor.name)

Sie könnten verwendenFunction.callee :

Die native arguments.callerMethode ist veraltet, wird jedoch von den meisten Browsern unterstützt Function.caller, die das tatsächlich aufrufende Objekt (seinen Code) zurückgeben: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ Funktion / Aufrufer? Redirectlocale = de-US & redirectslug = JavaScript% 2FReference% 2FGlobal_Objects% 2FFunction% 2Fcaller

Sie könnten eine Quellkarte erstellen :

Wenn Sie die Literalfunktionssignatur (den "Namen") und nicht das Objekt selbst benötigen, müssen Sie möglicherweise auf etwas Anpassungsfähigeres zurückgreifen, z. B. das Erstellen einer Array-Referenz der API-Zeichenfolgenwerte, die Sie benötigen häufig zugreifen. Sie können sie mithilfe von Object.keys()und Ihrer Zeichenfolge zusammen zuordnen oder in der Quellzuordnungsbibliothek von Mozilla auf GitHub nach größeren Projekten suchen: https://github.com/mozilla/source-map

Benny
quelle
2

Ich weiß, dass dies eine alte Frage ist, aber in letzter Zeit hatte ich ein ähnliches Problem, als ich versuchte, die Methoden einiger React Component für Debugging-Zwecke zu dekorieren. Wie bereits gesagt, arguments.callerund arguments.calleeim strengen Modus verboten, der wahrscheinlich standardmäßig in Ihrem React-Transpiling aktiviert ist. Sie können es entweder deaktivieren oder ich konnte einen anderen Hack entwickeln, da in React alle Klassenfunktionen benannt sind. Sie können dies tatsächlich tun:

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

Das hat bei mir funktioniert.

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

Testcode:

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

Ich hatte ein ähnliches Problem und löste es wie folgt:

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

Dieser Code implementiert auf bequemere Weise eine Antwort, die ich hier oben in dieser Diskussion bereits gelesen habe. Jetzt habe ich eine Mitgliedsfunktion, die den Namen eines Funktionsobjekts abruft. Hier ist das vollständige Skript ...

<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>
Sandro Rosa
quelle
1

Wenn ich verstanden habe, was Sie tun wollten, ist dies das, was ich in einem Funktionskonstruktor tue.

if (!(this instanceof arguments.callee)) {
    throw "ReferenceError: " + arguments.callee.name + " is not defined";
}
Zibri
quelle
2
Hinweis: Veraltet - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Helcio R. Macedo Pandelo
0

Dies funktioniert in ES5, ES6, allen Browsern und strengen Modusfunktionen.

So sieht es mit einer benannten Funktion aus.

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

So sieht es mit einer anonymen Funktion aus.

(() => {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at <anonymous>:2:15
Eric
quelle
Dies führt zu unterschiedlichen Ergebnissen pro Browser. Chrome: at myName (<anonymous>:2:15)Firefox:@debugger eval code:3:3
jkdev
(Funktion myName () {console.log (neuer Fehler (). stack.split (/ \ r \ n | \ r | \ n / g) [1] .trim (). split ("") [1]) ;}) ();
Zibri
-2

Schauen Sie hier: http://www.tek-tips.com/viewthread.cfm?qid=1209619

arguments.callee.toString();

scheint für Ihre Bedürfnisse richtig zu sein.

Manuel Bitto
quelle
3
Argumente.callee.toString () gibt nur die Quelle der Funktion zurück.
Scott Klarenbach
2
Nicht erlaubt: Auf die Eigenschaften 'Anrufer', 'Angerufene' und 'Argumente' kann möglicherweise nicht über Funktionen des strengen Modus oder die Argumente Objekte für Aufrufe
zugegriffen werden
-2

Mit Error.stack können Sie den Funktionsnamen und die genaue Position Ihrer Position verfolgen.

Siehe stacktrace.js

Kofifus
quelle
-3

Einfacher Weg, um den Funktionsnamen aus der Funktion heraus zu ermitteln, die Sie ausführen.

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

Dave Brown
quelle
1
gibt mir eine GUID
Tamir Daniely
1
Seien Sie sich bewusst, dass der Inhalt thisgrundsätzlich alles sein kann. versuchen(function(){console.log(this.name);}).bind({name: "put basically anything here even an object maybe a 1TB string maybe a class definition"})()
Valen