2016-06-30 69 views
7

Per chiunque la visione di questo, questa domanda è simile al seguente:Ottiene il nome di metodo di classe a macchina

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

Tuttavia è diverso in alcuni aspetti.

Sto cercando di ottenere il nome del metodo che appartiene a una classe e di memorizzarlo in una variabile in TypeScript/JavaScript.
Date un'occhiata al seguente configurazione:

class Foo { 

    bar(){ 
     // logic 
    } 

} 

Quanto sopra è valido dattiloscritto e vorrei creare un metodo in una classe diversa che mi restituirà il nome del metodo bar(), cioè "bar"
esempio :

class ClassHelper { 

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

} 

vorrei poi essere in grado di utilizzare il ClassHelper nel seguente modo:

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

Ho guardato un sacco di messaggi, alcuni suggeriscono usando la seguente:

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

ma questo viene a mancare come i miei metodi non iniziano con function
un altro suggerimento era:

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

Questo restituisce solo il nome della classe e, dalla lettura dei post, sembra che l'utilizzo di constructor non sia affidabile.

Inoltre, quando ho il debug del codice usando alcuni di questi metodi, passando nel metodo in questo modo: ClassHelper.getMethodName(foo.bar); si tradurrà nel parametro essere passato se il metodo prende uno, ad esempio:

class Foo { 

    bar(param: any){ 
     // logic 
    } 

} 

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

I Ho lottato con questo per un po ', se qualcuno ha qualche informazione su come posso risolvere questo sarebbe molto apprezzato.

mio .toString() sul metodo passata restituisce questo:

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

piuttosto che:

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

e secondo MDN che non è supposto per uno:

Quello is, toString decompila la funzione e la stringa restituita include la parola chiave function, l'elenco di argomenti, parentesi graffe e t lui fonte del corpo della funzione.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString#Description

risposta

2

Ho trovato una soluzione. Io non sono sicuro di come efficiente e riutilizzabile è, ma ha funzionato in più casi di test, incluso i metodi annidati, ad esempio Class -> Class -> Method

La mia soluzione:

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

La notazione lambda ClassHelper.getMethodName(() => foo.bar); è stato fondamentale per ottenere questo a lavoro che ha consentito il .toString() per contenere return foo.bar;

la prossima cosa che ho dovuto fare è stato quello di estrarre la chiamata al metodo dal .toString() poi ho usato funzioni per gli array e stringhe per restituire l'ultima stringa che inevitabilmente è il nome del metodo.

come ho detto, non è probabilmente la soluzione più elegante ma ha funzionato e anche lavorato per i metodi annidati

NOTA: È possibile sostituire la funzione lambda con una normale funzione anonima

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

Questa soluzione potrebbe avere uno svantaggio quando sono coinvolti vecchi browser e parole chiave riservate, dato che lambda potrebbe compilare 'foo [" default "]'. Altrimenti sembra buono. –

+0

è possibile modificare la funzione lambda in una funzione normale, credo: 'function() {return foo.bar; } ' – Colum

1

Purtroppo, il nome di metodi di classe tipografico è perso durante la compilazione di JS (come correttamente dedotto). I metodi typescript sono compilati in Javascript aggiungendo il metodo al prototipo di una classe Javascript. (Controlla il Javascript compilato per maggiori informazioni).

in JavaScript, questo funziona:

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

Quindi, la cosa si potrebbe pensare è invertire il prototipo della classe, in modo che la classe stessa diventa la chiave dell'oggetto:

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

Naturalmente, questa è una soluzione molto hacky, poiché

  1. complessità è O(N) (loop mediante array)
  2. Non sono sicuro che funzioni in ogni caso.

(Working) Esempio per il vostro problema:

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" 

La soluzione migliore sarebbe un'opzione del compilatore dattiloscritto che cambia il modo dattiloscritto transpiles classi a JavaScript. Tuttavia, al momento non sono a conoscenza di alcuna opzione che faccia questo genere di cose.

+0

questo è un buon lavoro in giro, ma non funziona se la classe è sconosciuta. Questo può sembrare arretrato e sbagliato, ma il modo in cui questo è implementato nel mio codice, una classe separata prende un parametro del metodo, che poi cerca di trovare il nome. il parametro può essere un metodo di qualsiasi classe. Penso di aver trovato una soluzione e attualmente sto lavorando su di esso – Colum

+0

Non dimenticare che la funzione viene convertita in rappresentazione di stringhe quando la inserisci in un oggetto come chiave. Questo fa sì che funzioni diverse con lo stesso contenuto si scontrino. –

1

Object.keys potrebbe aiutarti. Prova questo modo

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

È possibile passare sia l'oggetto e il metodo, ottenere la matrice di chiavi di proprietà sull'oggetto, quindi utilizzando chiavi di proprietà vedere se ogni data proprietà è lo stesso riferimento oggetto come il metodo fornito - e se è così, c'è una corrispondenza.

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

Questo ha l'inconveniente che l'host oggetto deve essere noto. Se il metodo non può essere trovato, verrà restituito undefined.

+0

Dopo aver provato questo approccio, il mio test unitario non è riuscito e quindi non ha fornito una risposta adeguata per la mia situazione specifica, tuttavia è un buon approccio e potrebbe funzionare per gli altri – Colum

+0

@Colum Potrebbe essere che lo stesso metodo esista in più proprietà, o quell'eredità è coinvolta? Per quest'ultimo, è possibile attraversare la catena del prototipo fino a raggiungere 'Object'. –

2

Ho preso l'idea di John White e l'ho migliorata in modo che funzioni per ogni caso a cui potessi pensare. Questo metodo ha il vantaggio di non dover analizzare il codice js in fase di runtime. Esiste tuttavia un caso limite, in cui semplicemente non è possibile dedurre il nome della proprietà corretta perché esistono più nomi di proprietà giusti.

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

Problemi correlati