Get functions (methods) of a class

84,032

Solution 1

This function will get all functions. Inherited or not, enumerable or not. All functions are included.

function getAllFuncs(toCheck) {
    const props = [];
    let obj = toCheck;
    do {
        props.push(...Object.getOwnPropertyNames(obj));
    } while (obj = Object.getPrototypeOf(obj));
    
    return props.sort().filter((e, i, arr) => { 
       if (e!=arr[i+1] && typeof toCheck[e] == 'function') return true;
    });
}

Do test

getAllFuncs([1,3]);

console output:

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

Note

It doesn't return functions defined via symbols;

Solution 2

The members of a class are not enumerable. To get them, you have to use Object.getOwnPropertyNames:

var propertyNames = Object.getOwnPropertyNames(Object.getPrototypeOf(foo));
// or
var propertyNames = Object.getOwnPropertyNames(Foo.prototype);

Of course this won't get inherited methods. There is no method that can give you all of them. You'd have to traverse the prototype chain and get the properties for each prototype individually.

Solution 3

ES6 adds Reflection which makes the code to do this a bit cleaner.

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

Solution 4

There were a few issues in @MuhammadUmer answer for me (symbols, index i+1, listing of Object methods, etc...) so taking inspiration from it, I came up with this

(warning Typescript compiled to 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
}

This function will list all methods of an instance of the class including those inherited, but not the constructor and those of the Object prototype.

Test

The function returns

[ 'asyncMethod',
  'echo',
  'generatorMethod',
  'ping',
  'pong',
  'anotherEcho' ]

listing the methods of an instance of TestClass (typescript)

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

Solution 5

To make members of class enumerable you can use Symbol.iterator

I had to get all allowed methods of object (including inherited). So i created class "Enumerable" and all my base classes inherited from him.

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);
    }
    // --------------
  }
}
Share:
84,032
Jan
Author by

Jan

Updated on June 20, 2021

Comments

  • Jan
    Jan almost 3 years

    I have to dynamically fetch the properties and functions of a ES6 class. Is this even possible?

    Using a for...in loop, I only get to loop through the properties of a class instance:

    class Foo {
      constructor() {
        this.bar = "hi";
      }
      someFunc() {
        console.log(this.bar);
      }
    }
    var foo = new Foo();
    for (var idx in foo) {
      console.log(idx);
    }
    

    Output:

    bar
    
  • Felix Kling
    Felix Kling almost 9 years
    You could probably stop at Object.prototype if we really only want to get class methods. Otherwise, nice :)
  • Muhammad Umer
    Muhammad Umer almost 9 years
    True, that would be useful in lots of conditions too.
  • loganfsmyth
    loganfsmyth almost 9 years
    You'll also need to .concat(Object.getOwnPropertySymbols(obj)) since getOwnPropertyNames will only return string keys. That means your example won't pick up an iterator function for example.
  • loganfsmyth
    loganfsmyth almost 9 years
    Don't forget Object.getOwnPropertySymbols too!
  • antitoxic
    antitoxic over 7 years
    Nice solution. If you want to remove builtin things like __defineGetter__ you can do while ((obj = Object.getPrototypeOf(obj)) && obj != Object.prototype)
  • AlexV
    AlexV about 7 years
    Nice, but obj inside the filter is null. If it wasn't the while will never exit, right :)
  • Muhammad Umer
    Muhammad Umer about 7 years
    Yes, when obj becomes undefined while loop stops
  • Wicharek
    Wicharek about 7 years
    That's a great snippet and it works. However, a small caveat: it might not work as expected if object has properties defined by Object.defineProperty or es5-style get propertyName() { }. The problem is here typeof obj[p] === 'function'. The thing is property obj[p] getter will actually get called, but with incorrect this. So if property getter uses this it will lead to unexpected results, e.g. crashes. Solution - here typeof obj[p] === 'function' instead of obj use the original one passed to this getAllMethods (store it in local variable).
  • Jules
    Jules almost 7 years
    Don't miss out the extra brackets in @antitoxic's suggestion... otherwise you start getting the prototype of true, which is apparently not null, so you go back to true again the next iteration...
  • DKebler
    DKebler over 6 years
    Ignore your linter about the assignment in while statement as it is needed
  • chrismarx
    chrismarx about 6 years
    This solution returns all the internal methods that I doubt people want, how would you get just the declared methods?
  • Muhammad Umer
    Muhammad Umer about 6 years
    @Wicharek can you give an example
  • Wicharek
    Wicharek about 6 years
    @MuhammadUmer here is the code I have successfully used in one of my projects
  • thrice801
    thrice801 almost 5 years
    These were close but still have issues with getters, namely everytime you are checking the getter you are actually calling it, which is a recipe for potential disaster, and was blowing up when I was trying to implement. Here is a version fixing that gist.github.com/jasonayre/5d9ebd64299bf69c8637a9e03e33a3fb
  • Vectorjohn
    Vectorjohn over 4 years
    How can this work? When the loop exits, obj is falsy, so your filter will always fail because of the typeof obj[e] == 'function' reference. You need to restore the original value of obj after the loop. Actual sample output: "TypeError: Cannot read property 'defineGetter' of null"
  • Muhammad Umer
    Muhammad Umer over 4 years
    I'm not sure what I understand, if obj has prop a which is a function then obj.a will be true.
  • Magico
    Magico almost 4 years
    Nice function works great, it there the possibility to list ONLY public methods?