ES6 Iterate over class methods

JavascriptEcmascript 6

Javascript Problem Overview


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);
}

Javascript Solutions


Solution 1 - Javascript

You can use Object.getOwnPropertyNames on the prototype:

Object.getOwnPropertyNames( Animal.prototype )
// [ 'constructor', 'getAnimalType' ]

Solution 2 - Javascript

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" ]

Solution 3 - Javascript

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]
}

Solution 4 - Javascript

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);
})

Solution 5 - Javascript

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'));
}

Solution 6 - Javascript

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);

Solution 7 - Javascript

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))

Solution 8 - Javascript

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 https://stackoverflow.com/questions/31054910/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.

Solution 9 - Javascript

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);
}

Solution 10 - Javascript

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>
  `;

Solution 11 - Javascript

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')
}

Solution 12 - Javascript

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
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionseasickView Question on Stackoverflow
Solution 1 - JavascriptPaulView Answer on Stackoverflow
Solution 2 - JavascriptgoofballLogicView Answer on Stackoverflow
Solution 3 - JavascriptLeo DutraView Answer on Stackoverflow
Solution 4 - JavascriptPavel YeView Answer on Stackoverflow
Solution 5 - JavascriptBasavView Answer on Stackoverflow
Solution 6 - JavascriptPonmudi VNView Answer on Stackoverflow
Solution 7 - JavascripthaouarinView Answer on Stackoverflow
Solution 8 - JavascriptJulesView Answer on Stackoverflow
Solution 9 - JavascriptlepeView Answer on Stackoverflow
Solution 10 - JavascriptChester FungView Answer on Stackoverflow
Solution 11 - JavascriptAronandaView Answer on Stackoverflow
Solution 12 - JavascriptCraig HarshbargerView Answer on Stackoverflow