【问题标题】:Get functions (methods) of a class [duplicate]获取类的函数(方法)[重复]
【发布时间】:2015-09-12 07:54:09
【问题描述】:

我必须动态获取 ES6 类的属性和函数。这甚至可能吗?

使用 for...in 循环,我只能遍历类实例的属性:

class Foo {
  constructor() {
    this.bar = "hi";
  }
  someFunc() {
    console.log(this.bar);
  }
}
var foo = new Foo();
for (var idx in foo) {
  console.log(idx);
}

输出:

bar

【问题讨论】:

  • Object.getOwnPropertyNames(foo).concat(Object.getOwnPropertyNames(foo.__proto__))
  • 看看我贴的函数,需要继承属性吗?

标签: javascript oop ecmascript-6


【解决方案1】:

类的成员不可枚举。要获得它们,您必须使用Object.getOwnPropertyNames

var propertyNames = Object.getOwnPropertyNames(Object.getPrototypeOf(foo));
// or
var propertyNames = Object.getOwnPropertyNames(Foo.prototype);

当然这不会得到继承的方法。没有任何方法可以为您提供所有这些。您必须遍历原型链并单独获取每个原型的属性。

【讨论】:

  • 别忘了Object.getOwnPropertySymbols
【解决方案2】:

这个函数会获取所有函数。继承与否,可枚举与否。包含所有功能。

function getAllFuncs(toCheck) {
    const props = [];
    let obj = toCheck;
    do {
        props.push(...Object.getOwnPropertyNames(obj));
    } while (obj = Object.getPrototypeOf(obj));
    
    return props.sort().filter((e, i, arr) => { 
       if (e!=arr[i+1] && typeof toCheck[e] == 'function') return true;
    });
}

做测试

getAllFuncs([1,3]);

控制台输出:

["constructor", "toString", "toLocaleString", "join", "pop", "push", "concat", "reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "forEach", "some", "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight", "entries", "keys", "constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__lookupGetter__", "__defineSetter__", "__lookupSetter__"]

注意

它不返回通过符号定义的函数;

【讨论】:

  • 如果我们真的只想获得class 方法,您可能会停在Object.prototype。否则,很好:)
  • 没错,这在很多情况下也很有用。
  • 您还需要.concat(Object.getOwnPropertySymbols(obj)),因为getOwnPropertyNames 只会返回string 键。这意味着您的示例不会使用迭代器函数。
  • 不错的解决方案。如果你想删除像 __defineGetter__ 这样的内置内容,你可以这样做 while ((obj = Object.getPrototypeOf(obj)) && obj != Object.prototype)
  • 很好,但过滤器中的objnull。如果不是while 将永远不会退出,对吧:)
【解决方案3】:

要使类的成员可枚举,您可以使用 Symbol.iterator

我必须获取所有允许的对象方法(包括继承的)。所以我创建了“Enumerable”类,我所有的基类都继承自他。

class Enumerable {
  constructor() {

    // Add this for enumerate ES6 class-methods
    var obj = this;

    var getProps = function* (object) {
      if (object !== Object.prototype) {
        for (let name of Object.getOwnPropertyNames(object)) {
          let method = object[name];
          // Supposedly you'd like to skip constructor and private methods (start with _ )
          if (method instanceof Function && name !== 'constructor' && name[0] !== '_')
            yield name;
        }
        yield* getProps(Object.getPrototypeOf(object));
      }
    }

    this[Symbol.iterator] = function*() {
      yield* getProps(obj);
    }
    // --------------
  }
}

【讨论】:

    【解决方案4】:

    @MuhammadUmer 对我的回答存在一些问题(符号、索引 i+1Object 方法列表等),因此我从中汲取灵感,想出了这个

    (警告 Typescript 编译为 ES6)

    const getAllMethods = (obj) => {
        let props = []
    
        do {
            const l = Object.getOwnPropertyNames(obj)
                .concat(Object.getOwnPropertySymbols(obj).map(s => s.toString()))
                .sort()
                .filter((p, i, arr) =>
                    typeof obj[p] === 'function' &&  //only the methods
                    p !== 'constructor' &&           //not the constructor
                    (i == 0 || p !== arr[i - 1]) &&  //not overriding in this prototype
                    props.indexOf(p) === -1          //not overridden in a child
                )
            props = props.concat(l)
        }
        while (
            (obj = Object.getPrototypeOf(obj)) &&   //walk-up the prototype chain
            Object.getPrototypeOf(obj)              //not the the Object prototype methods (hasOwnProperty, etc...)
        )
    
        return props
    }
    

    此函数将列出类实例的所有方法,包括继承的方法,但不构造函数和 Object 原型的方法。

    测试

    函数返回

    [ 'asyncMethod',
      'echo',
      'generatorMethod',
      'ping',
      'pong',
      'anotherEcho' ]
    

    列出TestClass 实例的方法(打字稿)

    class Echo  {
        echo(data: string): string {
            return data
        }
        anotherEcho(data: string): string {
            return `Echo ${data}`
        }
    }
    
    
    class TestClass extends Echo {
    
        ping(data: string): string {
            if (data === 'ping') {
                return 'pong'
            }
            throw new Error('"ping" was expected !')
        }
    
        pong(data: string): string {
            if (data === 'pong') {
                return 'ping'
            }
            throw new Error('"pong" was expected !')
        }
    
        //overridden echo
        echo(data: string): string {
            return 'blah'
        }
    
        async asyncMethod(): Promise<string> {
            return new Promise<string>((resolve: (value?: string) => void, reject: (reason?: any) => void) => {
                resolve('blah')
            })
        }
    
        * generatorMethod(): IterableIterator<string> {
            yield 'blah'
        }
    }
    

    【讨论】:

    • 这是一个很棒的 sn-p 并且它有效。但是,一个小警告:如果对象具有由 Object.defineProperty 或 es5 样式 get propertyName() { } 定义的属性,它可能无法按预期工作。问题在这里typeof obj[p] === 'function'。问题是属性obj[p] getter 实际上会被调用,但this 不正确。所以如果属性 getter 使用 this 会导致意想不到的结果,例如崩溃。解决方案 - 此处 typeof obj[p] === 'function' 而不是 obj 使用传递给此 getAllMethods 的原始值(将其存储在局部变量中)。
    • @Wicharek 你能举个例子吗
    • @MuhammadUmer here is the code我已经成功用于我的一个项目中
    • 这些很接近,但仍然存在 getter 问题,即每次检查实际调用的 getter 时,这是潜在灾难的根源,并且在我尝试实施时爆炸了。这是修复 gist.github.com/jasonayre/5d9ebd64299bf69c8637a9e03e33a3fb 的版本
    • 不错的函数效果很好,是否有可能只列出公共方法?
    【解决方案5】:

    ES6 添加了反射,这使得执行此操作的代码更简洁。

    function getAllMethodNames(obj) {
      let methods = new Set();
      while (obj = Reflect.getPrototypeOf(obj)) {
        let keys = Reflect.ownKeys(obj)
        keys.forEach((k) => methods.add(k));
      }
      return methods;
    }
    
    
    /// a simple class hierarchy to test getAllMethodNames
    
    
    // kind of like an abstract base class
    class Shape {
      constructor() {}
      area() {
        throw new Error("can't define area for generic shape, use a subclass")
      }
    }
    
    // Square: a shape with a sideLength property, an area function and getSideLength function
    class Square extends Shape {
      constructor(sideLength) {
        super();
        this.sideLength = sideLength;
      }
      area() {
        return this.sideLength * this.sideLength
      };
      getSideLength() {
        return this.sideLength
      };
    }
    
    // ColoredSquare: a square with a color
    class ColoredSquare extends Square {
      constructor(sideLength, color) {
        super(sideLength);
        this.color = color;
      }
      getColor() {
        return this.color
      }
    }
    
    
    let temp = new ColoredSquare(2, "red");
    let methods = getAllMethodNames(temp);
    console.log([...methods]);

    【讨论】:

    • 根据需要忽略关于 while 语句中赋值的 linter
    • 这个解决方案返回了我怀疑人们想要的所有内部方法,你如何只得到声明的方法?
    猜你喜欢
    • 2020-02-16
    • 2016-06-05
    • 1970-01-01
    • 2015-02-24
    • 2010-12-06
    • 2012-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多