Получить функции (методы) класса
Я должен динамически получать свойства и функции класса 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 ответов
Эта функция получит все функции. Унаследовано или нет, перечислимо или нет. Все функции включены.
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__"]
Заметка
Он не возвращает функции, определенные с помощью символов;
Члены класса не перечисляются. Чтобы получить их, вы должны использовать Object.getOwnPropertyNames
:
var propertyNames = Object.getOwnPropertyNames(Object.getPrototypeOf(foo));
// or
var propertyNames = Object.getOwnPropertyNames(Foo.prototype);
Конечно, это не получит унаследованные методы. Нет способа, который может дать вам все из них. Вам нужно будет пройти цепочку прототипов и получить свойства для каждого прототипа в отдельности.
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]);
В ответе @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
}
Эта функция выведет список всех методов экземпляра класса, включая наследуемые, кроме конструктора и методов прототипа 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'
}
}
Чтобы сделать членов класса перечисляемыми, вы можете использовать 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);
}
// --------------
}
}