在TypeScript中获取类方法的名称

时间:2016-06-30 08:46:48

标签: javascript regex typescript

对于观看此内容的任何人,此问题类似于以下内容:

Get an object's class name at runtime in TypeScript
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString#Description

然而,在一些方面它是不同的。

我希望得到属于类的方法的名称,并将其存储在TypeScript / JavaScript中的变量中。
看看以下设置:

class Foo {

    bar(){
        // logic
    }

}

以上是有效的TypeScript,我想在另一个类中创建一个方法,该方法将返回bar()方法的名称,即"bar"
例如:

class ClassHelper {

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

}

我希望能够以下列方式使用ClassHelper

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

我查看过很多帖子,有些建议使用以下内容:

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

但由于我的方法不以function开头,因此失败 另一个建议是:

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

这只返回了班级名称,但是从阅读帖子中看来,似乎使用constructor可能是不可靠的。

此外,当我使用其中一些方法调试代码时,传递方法如下:ClassHelper.getMethodName(foo.bar);将导致参数传递,如果方法采用一个,例如:

class Foo {

    bar(param: any){
        // logic
    }

}

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

我一直在努力解决这个问题,如果有人知道如何解决这个问题,我将不胜感激。

我传递的方法的.toString()会返回:

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

而不是:

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

根据MDN,它不应该:

  

也就是说,toString反编译该函数,返回的字符串包括函数关键字,参数列表,花括号和函数体的来源。
  http://git.lttng.org/?p=lttng-modules.git;a=shortlog;h=refs/heads/stable-2.8

6 个答案:

答案 0 :(得分:2)

我找到了解决方案。我不确定它的效率和可重用性,但它适用于多个测试用例,包括嵌套方法,例如Class -> Class -> Method

我的解决方案:

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表示法ClassHelper.getMethodName(() => foo.bar);是让这个工作变得有效的关键,因为它允许.toString()包含return foo.bar;

我接下来要做的是从.toString()中提取方法调用然后我使用数组和字符串函数来返回最后一个不可避免地是方法名称的子字符串。

就像我说的那样,它可能不是最优雅的解决方案,但它已经起作用,甚至适用于嵌套方法

注意:您可以使用常规匿名函数替换lambda函数

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

答案 1 :(得分:2)

我采用了约翰怀特的想法并对其进行了改进,因此它适用于我能想到的每一个案例。此方法的优点是无需在运行时解析js代码。但是有一个边缘情况,它根本无法推断出正确的属性名称,因为有多个正确的属性名称。

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

答案 2 :(得分:1)

不幸的是,在编译为JS时(正如你所推断的那样),Typescript类方法的名称会丢失。通过将方法添加到Javascript类的原型,将Typescript方法编译为Javascript。 (查看已编译的Javascript以获取更多信息)。

在Javascript中,这有效:

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

所以,你能想到的是扭转类的原型,所以类本身就成了对象的关键:

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

当然,这是一个非常hacky的解决方案,因为

  1. 复杂性为O(N)(循环数组)
  2. 我不确定这是否适用于所有情况。
  3. (工作)您的问题示例:

    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"
    

    更好的解决方案是使用typescript编译器选项来改变typescript将类转换为javascript的方式。但是,我目前还没有发现任何做这种事情的选择。

答案 3 :(得分:1)

Object.keys可能会对您有所帮助。 试试这种方式

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

答案 4 :(得分:1)

你可以传入对象和方法,获取对象上的属性键数组,然后使用属性键查看每个给定属性是否与提供的方法是相同的对象引用 - 如果是,则有一场比赛。

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

这具有必须知道主机对象的缺点。如果找不到该方法,则会返回undefined

答案 5 :(得分:0)

嗨,我一直在进行一些测试,我建立了一个更简单的解决方案。

class Parent{
  constructor(){
    // here you initialize your stuff
  }
   parentMethod1 (){ return "I am method 1" }
   parentMethod2 (){ return "I am method 2" }

}


class Child extends Parent{
  constructor(){
    // make this if you want to pass extra args to parent class
    super()
  }
  childMethod1(){  /*child actions*/ }
  childMethod2(){ /* other child actions */ }
}

const parent = new Parent();
const child = new Child();

console.log( Object.getOwnPropertyNames(parent.__proto__)) // --> ["constructor", "parentMethod1", "parentMethod2"]
console.log(Object.getOwnPropertyNames(child.__proto__)) //--> ["constructor", "childMethod1","childMethod2"]

console.log(parent.constructor.name) // --> Parent
console.log(child.constructor.name) // --> Child