Iterate through methods and properties of an ES6 class
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.
Yes it is possible
I use an util function that can:
- get method names of a not instanciated class
- of instanciated class
- that recursively gets all method of parent classes
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(B.prototype);
return allMethods.filter(name => name !== 'constructor' && !name.startsWith('__'))
}
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)]