javascript - Hämta namn på klassmetod i TypeScript
För alla som tittar på detta är denna fråga liknande den följande:
Hur får jag namnet på en objekts typ i JavaScript?
Få ett objekts klassnamn vid körning i TypeScript
Men det är annorlunda på några sätt.
Jag letar efter att få namnet på en metod som tillhör en klass och lagra det i en variabel i TypeScript/JavaScript.
Ta en titt på följande uppställning:
class Foo {
bar(){
// logic
}
}
Ovanstående är giltig TypeScript och jag skulle vilja skapa en metod i en annan klass som kommer att returnera mig namnet på bar()-metoden, dvs "bar"
t.ex:
class ClassHelper {
getMethodName(method: any){
return method.name; // per say
}
}
Jag skulle då vilja kunna använda ClassHelper på följande sätt:
var foo = new Foo();
var barName = ClassHelper.getMethodName(foo.bar); // "bar"
Jag har tittat på många inlägg, vissa föreslår att man använder följande:
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec(obj.toString());
var result = results && results.length > 1 && results[1];
men detta misslyckas eftersom mina metoder inte börjar med function
ett annat förslag var:
public getClassName() {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec(this["constructor"].toString());
return (results && results.length > 1) ? results[1] : "";
}
Detta returnerar dock endast klassnamnet och från att ha läst inlägg verkar det som att använda constructor kan vara opålitligt.
Dessutom, när jag har felsökt koden med några av dessa metoder, kommer det att skicka in metoden på följande sätt: ClassHelper.getMethodName(foo.bar); resultera i att parametern skickas om metoden tar en, t.ex.:
class Foo {
bar(param: any){
// logic
}
}
var foo = new Foo();
var barName = ClassHelper.getMethodName(foo.bar); // results in param getting passed through
Jag har kämpat med detta ett tag, om någon har någon information om hur jag kan lösa detta skulle det uppskattas mycket.
Min .toString() på den metod som skickades in returnerar detta:
.toString() = "function (param) { // code }"
snarare än:
.toString() = "function bar(param) { // code }"
och enligt MDN ska det inte heller göra det:
Det vill säga, toString dekompilerar funktionen, och den sträng som returneras inkluderar nyckelordet function, argumentlistan, måsvingarna och källkoden för funktionskroppen.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString#Description
Svar #1
Jag har tagit John Whites idé och förbättrat den så att den fungerar för varje fall jag kunde komma på. Denna metod har fördelen att den inte behöver parsa js-kod vid körning. Det finns dock ett specialfall, där den helt enkelt inte kan härleda rätt egenskapsnamn eftersom det finns flera rätta egenskapsnamn.
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));Svar #2
Jag hittade en lösning. Jag är inte säker på hur effektiv och återanvändbar den är, men den fungerade i flera testfall, inklusive nästlade metoder, t.ex. Class -> Class -> Method
Min lösning:
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);
Lambda-notationen ClassHelper.getMethodName(() => foo.bar); var avgörande för att få detta att fungera eftersom det tillät .toString() att innehålla return foo.bar;
Nästa sak jag var tvungen att göra var att extrahera metodanropet från .toString() och sedan använde jag array- och strängfunktioner för att returnera den sista delsträngen som oundvikligen är metodnamnet.
Som jag sa, det är förmodligen inte den mest eleganta lösningen men det har fungerat och har till och med fungerat för nästlade metoder
OBS: Du kan ersätta lambda-funktionen med en vanlig anonym funktion
var foo = new Foo();
var barName = ClassHelper.getMethodName(function() { return foo.bar; });
