Подтвердить что ты не робот

Получить функции (методы) класса

Мне нужно динамически извлекать свойства и функции класса ES6. Возможно ли это?

Используя цикл for 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
4b9b3361

Ответ 1

Эта функция получит все функции. Унаследовано или нет, перечислимое или нет. Все функции включены.

function getAllFuncs(obj) {
    var props = [];

    do {
        props = props.concat(Object.getOwnPropertyNames(obj));
    } while (obj = Object.getPrototypeOf(obj));

    return props.sort().filter(function(e, i, arr) { 
       if (e!=arr[i+1] && typeof obj[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__"]

Заметка

Он не возвращает функции, определенные через символы;

Ответ 2

Члены класса не перечислимы. Чтобы получить их, вы должны использовать Object.getOwnPropertyNames:

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

Конечно, это не приведет к унаследованным методам. Нет способа, который может дать вам все. Вам нужно будет пройти цепочку прототипов и получить свойства для каждого прототипа индивидуально.

Ответ 3

ES6 добавляет Reflection, что делает код для этого немного более чистым.

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

Ответ 4

В ответе @MuhammadUmer было несколько вопросов (символы, индекс i+1, перечисление методов Object и т.д.), поэтому, беря вдохновение из этого, я придумал этот

(предупреждение 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
}

Эта функция будет перечислять методы all экземпляра класса, в том числе унаследованные, , но конструктор и объекты прототипа объекта.

Test

Функция возвращает

[ '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'
    }
}

Ответ 5

Чтобы сделать членов класса перечислимыми, вы можете использовать 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);
    }
    // --------------
  }
}