Iterate through methods and properties of an ES6 class

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?

Upvotes: 22

Views: 20537

Answers (4)

TOPKAT
TOPKAT

Reputation: 8678

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

Upvotes: 5

Fred Truter
Fred Truter

Reputation: 666

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

Upvotes: 1

Alnitak
Alnitak

Reputation: 339816

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.

Upvotes: 42

KAKAN
KAKAN

Reputation: 29

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

Upvotes: -1

Related Questions