58

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);
}
seasick
  • 1,094
  • 2
  • 15
  • 29
  • 1
    Shouldn't iterating over `Animal.prototype` work? From what I understand, the underlying object architecture is still the same as the "old" way. – slebetman Jun 17 '15 at 03:08
  • Animal.prototype exposes the methods. I think the problem is in the way of iterating over them. for.. in does not seem to work. – seasick Jun 17 '15 at 03:10

12 Answers12

80

You can use Object.getOwnPropertyNames on the prototype:

Object.getOwnPropertyNames( Animal.prototype )
// [ 'constructor', 'getAnimalType' ]
Paul
  • 139,544
  • 27
  • 275
  • 264
  • I haven't tried this - but will this work on something that inherited? I'm not sure if it will... and only for the super class. – seasick Jun 17 '15 at 03:12
  • 12
    @seasick It works, but you will need to use `Object.getPrototypeOf` as well, to get to the prototype first: `Object.getOwnPropertyNames( Object.getPrototypeOf( cat ) )` – Paul Jun 17 '15 at 03:13
  • Thanks!!!! That definitely works, but considering the syntax of it all.. i wonder if this is the preferred way of doing something like this. What I am after is to just call all the methods of the class, but don't want to do it in a literal type fashion, so I wanted to iterate through them. – seasick Jun 17 '15 at 03:15
  • 4
    @seasick Oh, you would also have to recursively walk up the prototype chain to `getOwnPropertyNames` each object in the chain, if you want the methods that `Animal` inherits from some other base class. I don't think there is another way to get at the non-enumerable properties – Paul Jun 17 '15 at 03:15
  • Note that `getOwnPropertyNames` does not return inherited methods, if your class extends another, those methods will not be included. – Adam Reis Jan 12 '20 at 09:09
  • Also note that `getOwnPropertyNames` returns a sorted list, rather than reflecting the declaration order. – Mike 'Pomax' Kamermans Jul 21 '20 at 01:37
24

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" ]
goofballLogic
  • 37,883
  • 8
  • 44
  • 62
  • Should be the selected answer, it works for both ES6 and Dynamic object and ES6 inheritance. – ktutnik Feb 28 '18 at 01:43
  • 4
    x[ name ] will execute a getter. Guard against this with (Object.getOwnPropertyDescriptor(x, name) || {}).get || typeof x [ name ] == ... – Samuel Danielson Mar 23 '18 at 15:27
  • 1
    @goofballLogic you should check if property still exist. Properties can be deleted using `delete` command. – Yairopro Nov 14 '18 at 16:53
7

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'));
}
Basav
  • 3,176
  • 1
  • 22
  • 20
7

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]
}
Leo Dutra
  • 162
  • 3
  • 11
6

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);
})
Pa Ye
  • 1,779
  • 12
  • 22
2

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);
Ponmudi VN
  • 1,493
  • 1
  • 18
  • 22
2

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))
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
haouarin
  • 1,511
  • 1
  • 11
  • 8
1

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);
}
lepe
  • 24,677
  • 9
  • 99
  • 108
0

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.

Jules
  • 14,841
  • 9
  • 83
  • 130
0

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>
  `;
Chester Fung
  • 182
  • 1
  • 10
0

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')
}
Aronanda
  • 191
  • 1
  • 2
  • 3
-1

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 context[key] === 'function' && !objectPrototypeKeys.includes(key)) {
        functions[key] = (prototype as any)[key]
      }
    })

    prototype = Reflect.getPrototypeOf(prototype)
  }

  return functions
}
Craig Harshbarger
  • 1,863
  • 3
  • 18
  • 29