获取类的函数(方法)

IT技术 javascript oop ecmascript-6
2021-01-19 23:13:02

我必须动态获取 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
5个回答

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

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

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

也不要忘记Object.getOwnPropertySymbols
2021-04-01 23:13:02

此函数将获取所有函数。继承与否,可枚举与否。包括所有功能。

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__"]

笔记

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

不错的解决方案。如果你想删除内置的东西,__defineGetter__你可以做while ((obj = Object.getPrototypeOf(obj)) && obj != Object.prototype)
2021-03-14 23:13:02
您还需要这样做,.concat(Object.getOwnPropertySymbols(obj))因为getOwnPropertyNames只会返回string密钥。这意味着您的示例不会选择例如迭代器函数。
2021-03-19 23:13:02
Object.prototype如果我们真的只想获取class方法,您可能会停下否则,很好:)
2021-03-20 23:13:02
不错,但obj过滤器内部是null. 如果不是while永远不会退出,对吧:)
2021-03-20 23:13:02
确实,这在很多情况下也很有用。
2021-03-25 23:13:02

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
2021-03-12 23:13:02
这个解决方案返回了我怀疑人们想要的所有内部方法,你将如何获得声明的方法?
2021-03-22 23:13:02

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

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

测试

函数返回

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

列出TestClass(typescript)实例的方法

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'
    }
}
@Wicharek 你能举个例子吗
2021-03-14 23:13:02
@MuhammadUmer这是我在我的一个项目中成功使用的代码
2021-03-15 23:13:02
这些很接近,但仍然存在与 getter 相关的问题,即每次检查 getter 时实际上是在调用它,这是潜在灾难的秘诀,并且在我尝试实施时爆炸了。这是修复gist.github.com/jasonayre/5d9ebd64299bf69c8637a9e03e33a3fb的版本
2021-03-18 23:13:02
这是一个很棒的片段,并且有效。但是,有一点需要注意:如果 object 具有由Object.defineProperty或 es5-style定义的属性,则它可能无法按预期工作get propertyName() { }问题就在这里typeof obj[p] === 'function'问题是属性obj[p]getter 实际上会被调用,但不正确this所以如果属性 getter 使用this它会导致意想不到的结果,例如崩溃。解决方案 - 在这里typeof obj[p] === 'function'而不是obj使用传递给 this 的原始对象getAllMethods(将其存储在局部变量中)。
2021-04-04 23:13:02
不错的功能很好用,是否有可能只列出公共方法?
2021-04-07 23:13:02

要使类的成员可枚举,您可以使用 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);
    }
    // --------------
  }
}