ES6 Перебирать методы класса
Учитывая этот класс; Как бы я перебрать методы, которые он включает?
class Animal {
constructor(type){
this.animalType = type;
}
getAnimalType(){
console.log('this.animalType: ', this.animalType );
}
}
let cat = window.cat = new Animal('cat')
Я попробовал следующее безуспешно:
for (var each in Object.getPrototypeOf(cat) ){
console.log(each);
}
14 ответов
Вы можете использовать Object.getOwnPropertyNames в прототипе:
Object.getOwnPropertyNames( Animal.prototype )
// [ 'constructor', 'getAnimalType' ]
Я знаю, я знаю, но эй...
const isGetter = ( x, name ) => ( Object.getOwnPropertyDescriptor( x, name ) || {} ).get
const isFunction = ( x, name ) => typeof x[ name ] === "function";
const deepFunctions = x =>
x && x !== Object.prototype &&
Object.getOwnPropertyNames( x )
.filter( name => isGetter( x, name ) || isFunction( x, name ) )
.concat( deepFunctions( Object.getPrototypeOf( x ) ) || [] );
const distinctDeepFunctions = x => Array.from( new Set( deepFunctions( x ) ) );
const userFunctions = x => distinctDeepFunctions( x ).filter( name => name !== "constructor" && !~name.indexOf( "__" ) );
// example usage
class YourObject {
hello() { return "uk"; }
goodbye() { return "eu"; }
}
class MyObject extends YourObject {
hello() { return "ie"; }
get when() { return "soon"; }
}
const obj = new MyObject();
console.log( userFunctions( obj ) ); // [ "hello", "when", "goodbye" ]
Попробуйте этот способ, если вам нужны только функции (например, замена на _.functions)
function getInstanceMethodNames (obj) {
return Object
.getOwnPropertyNames (Object.getPrototypeOf (obj))
.filter(name => (name !== 'constructor' && typeof obj[name] === 'function'));
}
Это немного сложнее, но использует методы из всей цепочки прототипов.
function getAllMethodNames (obj, depth = Infinity) {
const methods = new Set()
while (depth-- && obj) {
for (const key of Reflect.ownKeys(obj)) {
methods.add(key)
}
obj = Reflect.getPrototypeOf(obj)
}
return [...methods]
}
Поскольку методы класса ES6 не перечисляются, у вас нет другого выбора, кроме как использовать Object.getOwnPropertyNames() для получения массива всех его свойств.
После этого есть несколько способов извлечения методов, самым простым из которых может быть использование Array.prototype.forEach ().
Проверьте следующий фрагмент:
Object.getOwnPropertyNames(Animal.prototype).forEach((value) => {
console.log(value);
})
Это расширенная версия ответа, предложенного здесь /questions/47609742/poluchit-funktsii-metodyi-klassa/47609754#47609754
Я добавляю параметр deep, deep= Infinity по умолчанию, чтобы извлечь все функции, включая родительские функции. deep =1 для извлечения прямых методов данного класса.
getAllMethods = function (obj, deep = Infinity) {
let props = []
while (
(obj = Object.getPrototypeOf(obj)) && // walk-up the prototype chain
Object.getPrototypeOf(obj) && // not the the Object prototype methods (hasOwnProperty, etc...)
deep !== 0
) {
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)
deep--
}
return props
}
class Foo {
method01 (){
}
method02 (){
}
}
class FooChield extends Foo {
method01(){
}
method03(){
}
}
console.log('All methods', getAllMethods(new FooChield()))
console.log('Direct methods', getAllMethods(new FooChield(),1))
enter code here
Проверь эту скрипку
https://jsfiddle.net/ponmudi/tqmya6ok/1/
class Animal {
constructor(type){
this.animalType = type;
}
getAnimalType(){
console.log('this.animalType: ', this.animalType );
}
}
let cat = new Animal('cat');
//by instance
document.getElementById('1').innerHTML = Object.getOwnPropertyNames(cat);
//by getting prototype from instance
document.getElementById('2').innerHTML = Object.getOwnPropertyNames(Object.getPrototypeOf(cat));
//by prototype
document.getElementById('3').innerHTML = Object.getOwnPropertyNames(Animal.prototype);
Другой способ получить их без перечисления конструктора и других свойств:
var getMethods = function(obj) {
const o = Reflect.getPrototypeOf(obj);
const x = Reflect.getPrototypeOf(o);
return Reflect.ownKeys(o).filter(it => Reflect.ownKeys(x).indexOf(it) < 0);
}
Для тех, кто использует Typescript и интересуется такими вопросами, вы можете сделать много вещей простым способом, используя метаданные, отражающие API. Например, вы можете добавить метаданные в ваши классы с помощью декораторов и получить информацию о методах классов, их свойствах и их типах. Typescript и его типы позволяют делать вещи, аналогичные скомпилированным языкам, таким как java, C# и их API для самоанализа.
Взгляните на эти ссылки для получения дополнительной информации:
https://www.typescriptlang.org/docs/handbook/decorators.html
Я добавлю свою версию здесь для тех, кому это может понадобиться. Эта версия получает все свойства и элементы типа function, исключая все, что существует в
Object.prototype
и возвращает объект с теми же ключами и функциями, которые с ними связаны.
Пример
class Alpha {
a = 'a'
b = () => 'b'
c = function() { return 'c' }
d() {
return 'd'
}
}
class Beta {
e = 'e'
f = () => 'f'
g = function() { return 'g' }
h() {
return 'h'
}
}
const functions = getAllFunction(new Beta())
// functions
// {
// b: () => 'b',
// c: function() { return 'c'},
// d: function() { return 'd' },
// f: () => 'f',
// g: function() { return 'g' },
// h: function() { return 'h' }
// }
function getAllFunctions(source) {
const functions = {}
const objectPrototypeKeys = Reflect.ownKeys(Object.prototype)
let prototype = Reflect.getPrototypeOf(source)
// properties
Reflect.ownKeys(source).forEach(key => {
if (typeof key === 'string' && typeof source[key] === 'function') {
functions[key] = source[key]
}
})
// methods
while (prototype && prototype !== Object.prototype) {
Reflect.ownKeys(prototype).forEach(key => {
if (typeof key === 'string' && typeof context[key] === 'function' && !objectPrototypeKeys.includes(key)) {
functions[key] = (prototype as any)[key]
}
})
prototype = Reflect.getPrototypeOf(prototype)
}
return functions
}
Почему ответы здесь такие сложные? Сделаем это просто.
Object.getOwnPropertyNames(...)
это стандартный и единственный способ получить имя ненаследуемого метода класса. [возврат в виде массива в алфавитном порядке]
Object.getPrototypeOf(...)
это стандартный и единственный способ получить наследуемый прототип. [вернуть как Class.prototype]
Итак, просто зацикливание
Object.getPrototypeOf(Object.getPrototypeOf(
Object.getPrototypeOf( Object.getPrototypeOf(
...
Object.getPrototypeOf( your_object )
...
))
))
пока его конструктор ===
Object
Object.getOwnPropertyNames(Object.getPrototypeOf(#INSTANCE OF CLASS HERE. COULD BE THIS TO REFER OWN CLASS INSTANCE#));
Если вам нужно получить методы суперкласса, вы можете вызвать Object.getPrototypeOf()
несколько раз, пока вы не найдете их все. Вы, вероятно, захотите остановиться, когда доберетесь до Object.prototype
потому что методы там фундаментальны, и вы обычно не хотите касаться их каким-либо кодом, который использует отражение.
Вопрос " Получить функции (методы)" класса имеет ответ, который включает в себя функцию для этого, но у него было несколько недостатков (включая использование условия цикла, которое имело побочный эффект при изменении аргумента функции, что, как я полагаю, делает два сомнительных выбор стиля кода в одной строке кода...), поэтому я переписал его здесь:
export function listMethodNames (object, downToClass = Object)
{
// based on code by Muhammad Umer, https://stackru.com/a/31055217/441899
let props = [];
for (let obj = object; obj !== null && obj !== downToClass.prototype; obj = Object.getPrototypeOf(obj))
{
props = props.concat(Object.getOwnPropertyNames(obj));
}
return props.sort().filter((e, i, arr) => e != arr[i+1] && typeof object[e] == 'function');
}
Помимо исправления ошибки в исходном коде (который не копировал объект в другую переменную для цикла, поэтому к тому времени, когда он использовался для фильтрации в обратной строке, он больше не действовал), это дает необязательный аргумент остановите итерацию в настраиваемом классе. По умолчанию Object
(так Object
методы исключены; если вы хотите включить их, вы можете использовать класс, которого нет в цепочке наследования... возможно, сделав маркерный класс, например class IncludeObjectMethods{}
может иметь смысл). Я также изменил do
цикл до ясности for
цикл и переписать старый стиль function ...
Функция фильтра в функцию стрелки ES6, чтобы сделать код более компактным.
Способ сделать это, получая унаследованные методы без методов Object и, возможно, геттеров:
const getMethods = (obj, getters) => {
const orig = obj
let keys = []
do {
keys = keys.concat(Object.getOwnPropertyNames(obj))
if (!getters)
keys = keys.filter(k => !(Object.getOwnPropertyDescriptor(obj, k) || {}).get)
obj = Object.getPrototypeOf(obj)
} while (obj && obj.constructor && obj.constructor.name !== 'Object')
return Array.from(new Set(keys))
.filter(k => k !== 'constructor' && typeof orig[k] === 'function')
}