2016-06-30 10 views
7

Für das jemand sehen, ist diese Frage ähnlich der folgenden:Get Name der Klasse Methode in Typoskript

How do I get the name of an object's type in JavaScript?
Get an object's class name at runtime in TypeScript

aber es unterscheidet sich in einigen Grüßen ist.

Ich bin auf der Suche nach dem Namen der Methode, die zu einer Klasse gehört und speichern Sie es in einer Variablen in TypeScript/JavaScript.
Werfen Sie einen Blick auf die folgende Einstellung:

class Foo { 

    bar(){ 
     // logic 
    } 

} 

Die oben gilt Typoskript, und ich möchte eine Methode in einer anderen Klasse erstellen, die mir den Namen des bar() Methode zurückkehren, dh "bar"
zB :

class ClassHelper { 

    getMethodName(method: any){ 
     return method.name; // per say 
    } 

} 

ich würde dann die ClassHelper in der folgenden Art und Weise zu verwenden, um die Lage sein mag:

var foo = new Foo(); 
var barName = ClassHelper.getMethodName(foo.bar); // "bar" 

Ich habe an vielen Stellen sah, schlagen einige folgendes mit:

var funcNameRegex = /function (.{1,})\(/; 
var results = (funcNameRegex).exec(obj.toString()); 
var result = results && results.length > 1 && results[1]; 

aber nicht als meine Methoden nicht beginnen mit function
ein weiterer Vorschlag war:

public getClassName() { 
    var funcNameRegex = /function (.{1,})\(/; 
    var results = (funcNameRegex).exec(this["constructor"].toString()); 
    return (results && results.length > 1) ? results[1] : ""; 
} 

Dies gibt jedoch nur den Klassennamen zurück, und beim Lesen von Posts scheint es mit constructor unzuverlässig zu sein.

Auch, wenn ich den Code debuggt haben einige dieser Methoden verwenden, wie dies in der Methode übergeben: ClassHelper.getMethodName(foo.bar); im Parameter führen wird übergeben wird, wenn die Methode nimmt man, zum Beispiel:

class Foo { 

    bar(param: any){ 
     // logic 
    } 

} 

var foo = new Foo(); 
var barName = ClassHelper.getMethodName(foo.bar); // results in param getting passed through 

I Ich habe schon eine Weile damit zu kämpfen. Wenn jemand irgendwelche Informationen darüber hat, wie ich das lösen kann, wäre das sehr zu begrüßen.

Mein .toString() über die Methode in gibt diese übergeben: statt

.toString() = "function (param) { // code }" 

:

.toString() = "function bar(param) { // code }" 

und nach MDN ist es nicht sollte entweder:

That toString dekompiliert die Funktion und die zurückgegebene Zeichenfolge enthält das Funktionsschlüsselwort, die Argumentliste, geschweifte Klammern und t Die Quelle des Funktionskörpers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString#Description

Antwort

2

fand ich eine Lösung. Ich bin nicht sicher, wie effizient und wiederverwendbar ist, aber es funktionierte in mehreren Testfällen, verschachtelte Methoden enthalten, zB Class -> Class -> Method

Meine Lösung:

class ClassHelpers { 
    getName(obj: any): string {  
     if (obj.name) { 
      return obj.name; 
     } 

     var funcNameRegex = /function (.{1,})\(/; 
     var results = (funcNameRegex).exec(obj.toString()); 
     var result = results && results.length > 1 && results[1]; 

     if(!result){ 
      funcNameRegex = /return .([^;]+)/; 
      results = (funcNameRegex).exec(obj.toString()); 
      result = results && results.length > 1 && results[1].split(".").pop(); 
     } 

     return result || ""; 
    } 
} 


class Foo { 

    bar(param: any){ 
     // logic 
    } 

} 

var foo = new Foo(); 
var barName = ClassHelper.getMethodName(() => foo.bar); 

Die Lambda-Notation ClassHelper.getMethodName(() => foo.bar); zu bekommen dieser Schlüssel war return foo.bar;

das nächste, was ich tun musste, war zu extrahieren, um den Methodenaufruf aus der .toString() dann habe ich Array und String-Funktionen enthalten arbeiten, wie es die .toString() erlaubt die letzte Teilkette zurückzukehren, die zwangsläufig den Namen der Methode ist.

Wie ich schon sagte, es ist wahrscheinlich nicht die eleganteste Lösung, aber es hat funktioniert und arbeitet auch für verschachtelte Methoden

Hinweis: Sie können die Lambda-Funktion mit einer regelmäßigen anonymen Funktion ersetzen

var foo = new Foo(); 
var barName = ClassHelper.getMethodName(function() { return foo.bar; }); 
+0

Diese Lösung kann einen Nachteil haben, wenn alte Browser und reservierte Schlüsselwörter beteiligt sind, da das Lambda möglicherweise zu 'foo [" default "]' compiliert. Ansonsten sieht das gut aus. –

+0

Sie könnten die Lambda-Funktion in eine reguläre Funktion ändern, ich glaube: 'function() {return foo.bar; } ' – Colum

1

Leider ist der Name Typoskript Klassenmethoden verloren, wenn JS (wie Sie richtig inferred) kompilieren. Typescript-Methoden werden in Javascript kompiliert, indem die Methode zum Prototyp einer JavaScript-Klasse hinzugefügt wird. (Überprüfen Sie das kompilierte Javascript für weitere Informationen).

In Javascript, das funktioniert:

Foo.prototype["bar"] // returns foo.bar <<the function>> 

Also, das, was Sie von ist die Umkehrung der Prototyp der Klasse denken konnte, so die Klasse selbst wird der Schlüssel des Objekts:

Foo.prototype[foo.bar] // return "bar" 

natürlich ist dies eine sehr hacky Lösung, da

  1. Complexity O(N) (Schleife durch Array)
  2. Ich bin mir nicht sicher, dass das in jedem Fall funktioniert.

(Working) Beispiel für Ihr Problem:

class Foo{ 
    bar(){} 
} 

class ClassHelper{ 
    static reversePrototype(cls:any){ 
     let r = {}; 
     for (var key in cls.prototype){ 
      r[cls.prototype[key]] = key; 
     } 
     return r; 
    } 

    static getMethodNameOf(cls: any, method:any):string{ 
     let reverseObject = ClassHelper.reversePrototype(cls); 
     return reverseObject[method]; 
    } 
} 

var foo = new Foo(); 
console.log(ClassHelper.getMethodNameOf(Foo, foo.bar)) // "bar" 

Die bessere Lösung wäre eine Typoskript-Compiler-Option, die die Art und Weise Typoskript transpiles Klassen Javascript ändert. Allerdings ist mir derzeit keine Option bekannt, die solche Dinge tut.

+0

ist dies eine gute Arbeit um, aber es funktioniert nicht, wenn die Klasse nicht bekannt ist. Das mag rückwärts und falsch erscheinen, aber die Art und Weise, wie dies in meinem Code implementiert wird, nimmt eine separate Klasse einen Parameter der Methode, der dann versucht, den Namen zu finden. Der Parameter kann eine Methode jeder Klasse sein. Ich denke, dass ich eine Lösung gefunden haben kann und ich arbeite gerade daran – Colum

+0

Vergessen Sie nicht, dass die Funktion in Zeichenfolgendarstellung umgewandelt wird, wenn Sie es in einem Objekt als Schlüssel setzen. Das bringt verschiedene Funktionen mit dem gleichen Inhalt kollidieren. –

1

Object.keys kann Ihnen helfen. Versuchen Sie, diese Art und Weise

class Greeter { 
    test : Function; 

    constructor(message: string) { 

     this.test = function(){ 
      return message; 
     } 
    }  
} 

var a = new Greeter("world"); 

var properties = Object.keys(a); 
alert(properties.join(', ')); //test 
1

Sie sowohl das Objekt und die Methode übergeben konnten, auf dem Objekt das Array von Eigenschaftsschlüssel erhält, dann Eigenschaft Tasten sehen, ob jede gegebene Eigenschaft die gleiche Objekt Referenz als gelieferte Methode ist - und wenn ja, gibt es eine Übereinstimmung.

class ClassHelper { 
    getMethodName(obj: any, method: any) { 
     var methodName: string; 

     if (method) { 
      Object.keys(obj).forEach(key => { 
       if (obj[key] === method) { 
        methodName = key; 
       } 
      }); 
     } 

     return methodName; 
    } 
} 

Dies hat den Nachteil, dass der Host Objekt auch bekannt sein müssen. Wenn die Methode nicht gefunden werden kann, wird undefined zurückgegeben.

+0

Nach dem Versuch dieses Ansatzes meine Unit-Tests fehlgeschlagen, und daher keine passende Antwort für meine spezifische Situation, aber es ist ein guter Ansatz und kann für andere arbeiten – Colum

+0

@Colum Könnte es sein, dass die gleiche Methode in mehreren Eigenschaften existiert, oder diese Vererbung ist beteiligt? Für Letzteres könnten Sie die Prototypkette durchlaufen, bis Sie "Objekt" erreichen. –

2

Ich habe John Whites Idee genommen und verbessert, so dass sie für jeden Fall funktioniert, den ich mir vorstellen kann. Diese Methode hat den Vorteil, dass sie zur Laufzeit keinen js-Code parsen muss. Es gibt jedoch einen Randfall, bei dem der richtige Eigenschaftsname einfach nicht abgeleitet werden kann, da mehrere richtige Eigenschaftsnamen vorhanden sind.

class Foo { 
 
    bar() {} 
 
    foo() {} 
 
} 
 

 
class ClassHelper { 
 
    static getMethodName(obj, method) { 
 
    var methodName = null; 
 
    Object.getOwnPropertyNames(obj).forEach(prop => { 
 
     if (obj[prop] === method) { 
 
     methodName = prop; 
 
     } 
 
    }); 
 

 
    if (methodName !== null) { 
 
     return methodName; 
 
    } 
 
    
 
    var proto = Object.getPrototypeOf(obj); 
 
    if (proto) { 
 
     return ClassHelper.getMethodName(proto, method); 
 
    } 
 
    return null; 
 
    } 
 
} 
 

 
var foo = new Foo(); 
 
console.log(ClassHelper.getMethodName(foo, foo.bar)); 
 
console.log(ClassHelper.getMethodName(Foo.prototype, foo.bar)); 
 
console.log(ClassHelper.getMethodName(Foo.prototype, Foo.prototype.bar)); 
 

 
var edgeCase = { bar(){}, foo(){} }; 
 
edgeCase.foo = edgeCase.bar; 
 
console.log(ClassHelper.getMethodName(edgeCase, edgeCase.bar));