20

So with the growth of new frameworks with JavaScript many have adopted ECMAScript 6 shim's or TypeScript, with many new features. My question is this:

How does one iterate over the methods/properties of an ES6 class?

e.g. (with objects)

var obj = {
  prop: 'this is a property',
  something: 256,
  method: function() { console.log('you have invoked a method'); }
}

for (var key in obj) {
  console.log(key);
}

// => 'prop'
// => 'something'
// => 'method'

(with classes)

class MyClass {
  constructor() {
    this.prop = 'prop';
    this.something = 256;
  }

  method() {
    console.log('you have invoked a method');
  }
}

How do I list the methods MyClass has, and optionally its properties as well?

4 Answers4

38

The constructor and any defined methods are non-enumerable properties of the class's prototype object.

You can therefore get an array of the names (without constructing an instance of the class) with:

Object.getOwnPropertyNames(MyClass.prototype)

You cannot obtain the properties without creating an instance, but having done so you can use the Object.keys function which returns only the enumerable properties of an object:

Object.keys(myInstance)

AFAIK there's no standard way to obtain both the non-enumerable properties from the prototype and the enumerable properties of the instance together.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • Hi, your second suggestion doesn't work for me. Here is a codePen example https://codepen.io/seb-rom-o/pen/xxVxxGN?editors=0012 Did I do something wrong ? – TOPKAT Aug 04 '20 at 16:27
  • @538ROMEO yes - your `class A` _has no properties_. Please read the text above again - you need to use both the first to obtain the methods _and_ the second to obtain the properties. – Alnitak Aug 05 '20 at 13:12
4

To go further, here is a function that can get method names:

  • of a not instanciated class
  • of instanciated class
  • of all parent classes recursively

Which is not possible with Object.getOwnPropertyNames or Object.keys

Use it like:

class A {
    fn1() { }
}

class B extends A {
    fn2() { }
}

const instanciatedB = new B();

console.log(getClassMethodNames(B)) // [ 'fn2' ]
console.log(getClassMethodNames(instanciatedB)) // [ 'fn2', 'fn1' ]

Here is the util function code:

function getClassMethodNames(klass) {
    const isGetter = (x, name) => (Object.getOwnPropertyDescriptor(x, name) || {}).get;
    const isFunction = (x, name) => typeof x[name] === 'function';
    const deepFunctions = x =>
        x !== Object.prototype &&
        Object.getOwnPropertyNames(x)
            .filter(name => isGetter(x, name) || isFunction(x, name))
            .concat(deepFunctions(Object.getPrototypeOf(x)) || []);
    const distinctDeepFunctions = klass => Array.from(new Set(deepFunctions(klass)));

    const allMethods = typeof klass.prototype === "undefined" ? distinctDeepFunctions(klass) : Object.getOwnPropertyNames(klass.prototype);
    return allMethods.filter(name => name !== 'constructor' && !name.startsWith('__'))
}
TOPKAT
  • 6,667
  • 2
  • 44
  • 72
1

There is a way to find the names of the methods only. The following has been tested in nodeJS v10.9.0 with no special flags.

First we inject a new method into Object.

Object.methods = function(klass) {
  const properties = Object.getOwnPropertyNames(klass.prototype)
  properties.push(...Object.getOwnPropertySymbols(klass.prototype))
  return properties.filter(name => {
    const descriptor = Object.getOwnPropertyDescriptor(klass.prototype, name)
    if (!descriptor) return false
    return 'function' == typeof descriptor.value && name != 'constructor'
  })
}

You can see above that it is necessary to specifically exclude the constructor as it is not strictly a method of the class.

Create some class containing a constructor, accessors and methods

class Test {
  constructor(x, y) {
    this.x = x
    this.y = y
  }
  sum() { return x + y }
  distanceFromOrigin() { return Math.sqrt(this.squareX + this.squareY) }
  get squareX() { return this.x * this.x }
  get squareY() { return this.y * this.y }
  [Symbol.iterator]() {
    return null // TODO
  }
}

Let's see how this works

> console.log(Object.methods(Test))

Array(3) ["sum", "distanceFromOrigin", Symbol(Symbol.iterator)]
Fred Truter
  • 667
  • 4
  • 10
-1

I've not tested, still I think that there are 2 ways to do it. 1st one is to return the 'this' enviroment and loop over it. 2nd one is the same as Javascript's object. Example for 1st one:- (untested)

class MyClass {
  constructor() {
    this.prop = 'prop';
    this.something = 256;
  }

  method() {
    console.log('you have invoked a method');
  }
  get getthis()
  {
    return this;
  }
}

for( var key in MyClass.getthis )
{
    console.log(key);
}

this is the second method:-( untested )

class MyClass {
  constructor() {
    this.prop = 'prop';
    this.something = 256;
  }

  method() {
    console.log('you have invoked a method');
  }

}

for( var key in MyClass )
{
    console.log(key);
}
KAKAN
  • 29
  • 5
  • Yeah, I already tried your 2nd approach, which doesn't work. The first, won't work at all until you instantiate a new instance of the class `var myclass = new MyClass()`. And even after then, it only returns the properties, not the methods on the class - which is what I was looking for... – Aᴄʜᴇʀᴏɴғᴀɪʟ Jun 12 '16 at 06:47
  • Maybe we can do a loop inside the class and return it as an object? Have you tried that too? – KAKAN Jun 12 '16 at 06:53