Reputation: 1225
Given this class; how would i iterate over the methods that it includes?
class Animal {
constructor(type){
this.animalType = type;
}
getAnimalType(){
console.log('this.animalType: ', this.animalType );
}
}
let cat = window.cat = new Animal('cat')
What I have tried is the following with no success:
for (var each in Object.getPrototypeOf(cat) ){
console.log(each);
}
Upvotes: 60
Views: 25426
Reputation: 2243
I'll add my version here for anyone else who might need it. This version gets all properties and members that are of the type function while excluding anything that exists on Object.prototype
and returns an object with the same keys and the functions that go with them.
Example
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 prototype[key] === 'function' && !objectPrototypeKeys.includes(key)) {
functions[key] = (prototype as any)[key]
}
})
prototype = Reflect.getPrototypeOf(prototype)
}
return functions
}
Upvotes: 1
Reputation: 1601
This is an enhanced version of the answer suggested here https://stackoverflow.com/a/35033472/3811640
I add the parameter deep, deep= Infinity by default to extract all the functions including parent functions. deep =1 to extract the direct methods of a given class.
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))
Upvotes: 2
Reputation: 301
A way to do it getting inherited methods without the Object methods and optionally the getters:
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')
}
Upvotes: 0
Reputation: 370
Why the answers here are so complicated? Let's make it simple.
Object.getOwnPropertyNames(...)
is the standard and only way to get the name of non-inherit class methods. [return as an array in alphabetical order]
Object.getPrototypeOf(...)
is the standard and only way to get the inherit prototype. [return as an Class.prototype]
So, just looping
Object.getPrototypeOf(Object.getPrototypeOf(
Object.getPrototypeOf( Object.getPrototypeOf(
...
Object.getPrototypeOf( your_object )
...
))
))
until its constructor === Object
// JavaScript
class A {
myWife() { console.log(4) }
}
class B extends A {
myParent() { console.log(5) }
}
class C extends B {
myBrother() { console.log(6) }
}
class D extends C {
mySelf() { console.log(7) }
}
let obj = new D;
function classMethodsA(obj) {
let res = {};
let p = Object.getPrototypeOf(obj)
while (p.constructor !== Object) {
for(const k of Object.getOwnPropertyNames(p)){
if (!(k in res)) res[k] = obj[k];
}
p = Object.getPrototypeOf(p)
}
return res;
}
function classMethodsB(obj) {
let res = [];
let p = Object.getPrototypeOf(obj);
while (p.constructor !== Object) {
for(const k of Object.getOwnPropertyNames(p)){
if (!res.includes(k)) res.push(k);
}
p = Object.getPrototypeOf(p);
}
return res;
}
document.body.innerHTML=`
<p> -- No Constructor -- ${Object.keys(classMethodsA(obj))}</p>
<p> -- With Constructor -- ${classMethodsB(obj)}</p>
`;
Upvotes: 1
Reputation: 25200
Another way to get them without listing the constructor and other properties:
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);
}
Upvotes: 1
Reputation: 3406
Try this one linear way, if you need just the functions (like replacement to _.functions)
function getInstanceMethodNames (obj) {
return Object
.getOwnPropertyNames (Object.getPrototypeOf (obj))
.filter(name => (name !== 'constructor' && typeof obj[name] === 'function'));
}
Upvotes: 7
Reputation: 182
This is a bit more elaborated but gets methods from the whole prototype chain.
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]
}
Upvotes: 7
Reputation: 40489
i know, i know, but hey...
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" ]
Upvotes: 25
Reputation: 1838
Since methods on ES6 class are non-enumerable, you have no other option but to use Object.getOwnPropertyNames() to get an array of all its properties.
After achieving that, there are several ways to go about extracting methods, the simplest of which might be using Array.prototype.forEach().
Check out the following snippet:
Object.getOwnPropertyNames(Animal.prototype).forEach((value) => {
console.log(value);
})
Upvotes: 6
Reputation: 15199
If you need to get superclass methods as well, you can call Object.getPrototypeOf()
repeatedly until you find them all. You'll probably want to stop when you get to Object.prototype
, because the methods in there are fundamental and you usually don't want to touch them with any code that uses reflection.
The question Get functions (methods) of a class has an answer that includes a function for doing this, but it had a few shortcomings (including using a loop condition which had a side effect of modifying a function argument, which I figure makes two questionable code style choices in a single line of code...), so I've rewritten it here:
export function listMethodNames (object, downToClass = Object)
{
// based on code by Muhammad Umer, https://stackoverflow.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');
}
As well as fixing the bug in the original code (which didn't copy the object into another variable for the loop, so by the time it was used for filtering in the return line it was no longer valid) this gives an optional argument to stop the iteration at a configurable class. It'll default to Object
(so Object
's methods are excluded; if you want to include them you can use a class that doesn't appear in the inheritance chain... perhaps making a marker class e.g. class IncludeObjectMethods{}
might make sense). I've also changed the do
loop to a clearer for
loop and rewritten the old-style function ...
filter function into an ES6 arrow function to make the code more compact.
Upvotes: 0
Reputation: 1553
check this fiddle
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);
Upvotes: 2
Reputation: 141827
You can use Object.getOwnPropertyNames on the prototype:
Object.getOwnPropertyNames( Animal.prototype )
// [ 'constructor', 'getAnimalType' ]
Upvotes: 82