Получить функции (методы) класса
Мне нужно динамически извлекать свойства и функции класса 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
Ответы
Ответ 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);
}
// --------------
}
}