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?
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)]
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);
}
Related
This question already has answers here:
How to define method in javascript on Array.prototype and Object.prototype so that it doesn't appear in for in loop
(4 answers)
Closed 5 years ago.
When I managed to define a function inside prototype object, but when I use foreach loop on the props I found this method as one of my props either.
I wanted to know if I can define methods like the toString() mehtod?
the hasOwnProperty() if inside the loop is not an option for me because the loop is inside a package.
Here is my code.
function Person (personProps) {
Object.assign(this, {...personProps});
console.log(this);
}
Person.prototype.sayHay = () => {
console.log("hay");
}
const createPerson = (personProps) => {
let x = new Person(personProps);
x.sayHay();
return x;
};
export default createPerson;
Thanks for helping.
You can define property on the Person prototype like so -
function Person(personProps) {
Object.assign(this, { ...personProps});
}
Object.defineProperty(Person.prototype, "sayHay", {
value: function() {
console.log("hay");
},
/*
You can make `enumerable` property below false,
if you don't want it to be shown while
looping over properties
*/
enumerable: true
});
const createPerson = (personProps) => {
let x = new Person(personProps);
return x;
};
let person = createPerson({
a: 1
})
person.sayHay();
for (p in person) {
console.log(p);
}
This way you don't have to define sayHay property each time you create the object.
You can use a class
class Person {
constructor(personProps) {
Object.assign(this, { ...personProps});
}
sayHay() {
console.log("hay");
}
}
const createPerson = (personProps) => {
let x = new Person(personProps);
x.sayHay();
return x;
};
let person = createPerson({
name: 'John'
});
for (prop in person) {
console.log(prop);
}
Given class
class Test {
test() {
console.log('test called');
}
}
And some object toExtend = {}
How can I extend this object so it will have test method?
Object.assign ( as well as _.extend, _.assign, $.extend) do not copy methods. What is preferable way to do that?
Note that toExtend is passed from outside
UPD:
toExtend is instance of another class and has it's own prototype's methods
Object Literals
For object literals, which start with no protoype of their own (Object.getPrototypeOf(toExtend) === Object.protoype)), you can simply use Object.setPrototypeOf to extend the object:
class Test {
test() {
console.log('test called');
}
}
const toExtend = {};
// Set the prototype, so you "inherit" methods:
Object.setPrototypeOf(toExtend, Test.prototype);
toExtend.test();
In older runtimes, you would have to manually assign the prototype:
function Test() {
// noop ctor
}
Test.prototype.test = function() {
console.log('test called');
};
var toExtend = {};
// Set the prototype, so you "inherit" methods:
toExtend.__proto__ = Test.prototype;
toExtend.test();
Class Instances
For instances of an existing class, things are significantly more complex. They do have a prototype of their own, potentially with properties that must be copied, so you need to walk through those:
class Foo {
test() {
console.log('test');
}
}
class Bar {
toast() {
console.log('toast');
}
}
function dynamicExtend(target, base) {
const baseProto = Object.getPrototypeOf(target);
if (baseProto == Object.prototype) {
// simple case: no existing prototype
Object.setPrototypeOf(target, base.prototype);
} else {
// complex case: existing prototype
const proxyClass = class extends base {};
const proxyProto = proxyClass.prototype;
// assign the target properties
Object.getOwnPropertyNames(baseProto).forEach(n => {
const desc = Object.getOwnPropertyDescriptor(baseProto, n);
Object.defineProperty(proxyProto, n, desc);
});
Object.setPrototypeOf(target, proxyProto);
}
}
const targets = [{},
new Bar()
];
targets.forEach(t => {
dynamicExtend(t, Foo);
t.test();
if (t.toast) {
t.toast();
}
});
Note that, thanks to the proxy class, this does break instanceof style inheritance checks.
__proto__
As #PatrickRoberts noted in the comments, __proto__ is deprecated, so you should prefer setPrototypeOf whenever possible.
This question already has answers here:
ES6 Iterate over class methods
(12 answers)
Closed 5 years ago.
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
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.
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;
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]);
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'
}
}
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);
}
// --------------
}
}
Here is the current method I use, where everything is either all public or private. However I would like to differentiate between static and instance. How would I add this in?
obj holds the class or object have you, and config_module determines what type of module this is.
All private, All public ( a collection of statics ), or instance based on a constructor method.
If I do use an instance based, how do I differentiate between statics and instance based properties?
$P.support = $P.parsel = function (obj, config_module) {
$R.Parsel[obj.Name] = obj;
// all properties are private
if (!config_module) {
return undefined;
}
// all properties are public
if (config_module === true) {
return obj;
}
// constructor based, all properties are public
if (config_module === 'constructor') {
var object_public;
if (obj.constructor) {
object_public = obj.constructor;
delete obj.constructor;
}
$A.someKey(obj, function (val, key) {
// like this ?
if (/^s_/.test(key)) {
object_public[key] = val;
// like this ?
} else if (/^p_/.test(key)) {
object_public.prototype[key] = val;
} else {
object_public.prototype[key] = val;
}
});
return object_public;
}
};
You can have (pseudo) static stuff by adding properties to the constructor:
function Something(){}
Something.getStaticFoo = function(){ return 'foo'; }
Something.getStaticFoo();
var instance = new Something();
instance.getStaticFoo(); // error
If I understand your code, that's the same as object_public[key] = val;.
I can't seem to find the way to overload the [] operator in javascript. Anyone out there know?
I was thinking on the lines of ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
or am I not looking at the right things.
You can do this with ES6 Proxy (available in all modern browsers)
var handler = {
get: function(target, name) {
return "Hello, " + name;
}
};
var proxy = new Proxy({}, handler);
console.log(proxy.world); // output: Hello, world
console.log(proxy[123]); // output: Hello, 123
Check details on MDN.
You can't overload operators in JavaScript.
It was proposed for ECMAScript 4 but rejected.
I don't think you'll see it anytime soon.
The simple answer is that JavaScript allows access to children of an Object via the square brackets.
So you could define your class:
MyClass = function(){
// Set some defaults that belong to the class via dot syntax or array syntax.
this.some_property = 'my value is a string';
this['another_property'] = 'i am also a string';
this[0] = 1;
};
You will then be able to access the members on any instances of your class with either syntax.
foo = new MyClass();
foo.some_property; // Returns 'my value is a string'
foo['some_property']; // Returns 'my value is a string'
foo.another_property; // Returns 'i am also a string'
foo['another_property']; // Also returns 'i am also a string'
foo.0; // Syntax Error
foo[0]; // Returns 1
foo['0']; // Returns 1
Use a proxy. It was mentioned elsewhere in the answers but I think that this is a better example:
var handler = {
get: function(target, name) {
if (name in target) {
return target[name];
}
if (name == 'length') {
return Infinity;
}
return name * name;
}
};
var p = new Proxy({}, handler);
p[4]; //returns 16, which is the square of 4.
We can proxy get | set methods directly. Inspired by this.
class Foo {
constructor(v) {
this.data = v
return new Proxy(this, {
get: (obj, key) => {
if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
return obj.data[key]
else
return obj[key]
},
set: (obj, key, value) => {
if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
return obj.data[key] = value
else
return obj[key] = value
}
})
}
}
var foo = new Foo([])
foo.data = [0, 0, 0]
foo[0] = 1
console.log(foo[0]) // 1
console.log(foo.data) // [1, 0, 0]
As brackets operator is actually property access operator, you can hook on it with getters and setters. For IE you will have to use Object.defineProperty() instead. Example:
var obj = {
get attr() { alert("Getter called!"); return 1; },
set attr(value) { alert("Setter called!"); return value; }
};
obj.attr = 123;
The same for IE8+:
Object.defineProperty("attr", {
get: function() { alert("Getter called!"); return 1; },
set: function(value) { alert("Setter called!"); return value; }
});
For IE5-7 there's onpropertychange event only, which works for DOM elements, but not for other objects.
The drawback of the method is you can only hook on requests to predefined set of properties, not on arbitrary property without any predefined name.
one sneaky way to do this is by extending the language itself.
step 1
define a custom indexing convention, let's call it, "[]".
var MyClass = function MyClass(n) {
this.myArray = Array.from(Array(n).keys()).map(a => 0);
};
Object.defineProperty(MyClass.prototype, "[]", {
value: function(index) {
return this.myArray[index];
}
});
...
var foo = new MyClass(1024);
console.log(foo["[]"](0));
step 2
define a new eval implementation. (don't do this this way, but it's a proof of concept).
var MyClass = function MyClass(length, defaultValue) {
this.myArray = Array.from(Array(length).keys()).map(a => defaultValue);
};
Object.defineProperty(MyClass.prototype, "[]", {
value: function(index) {
return this.myArray[index];
}
});
var foo = new MyClass(1024, 1337);
console.log(foo["[]"](0));
var mini_eval = function(program) {
var esprima = require("esprima");
var tokens = esprima.tokenize(program);
if (tokens.length == 4) {
var types = tokens.map(a => a.type);
var values = tokens.map(a => a.value);
if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
if (values[1] == '[' && values[3] == ']') {
var target = eval(values[0]);
var i = eval(values[2]);
// higher priority than []
if (target.hasOwnProperty('[]')) {
return target['[]'](i);
} else {
return target[i];
}
return eval(values[0])();
} else {
return undefined;
}
} else {
return undefined;
}
} else {
return undefined;
}
};
mini_eval("foo[33]");
the above won't work for more complex indexes but it can be with stronger parsing.
alternative:
instead of resorting to creating your own superset language, you can instead compile your notation to the existing language, then eval it. This reduces the parsing overhead to native after the first time you use it.
var compile = function(program) {
var esprima = require("esprima");
var tokens = esprima.tokenize(program);
if (tokens.length == 4) {
var types = tokens.map(a => a.type);
var values = tokens.map(a => a.value);
if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
if (values[1] == '[' && values[3] == ']') {
var target = values[0];
var i = values[2];
// higher priority than []
return `
(${target}['[]'])
? ${target}['[]'](${i})
: ${target}[${i}]`
} else {
return 'undefined';
}
} else {
return 'undefined';
}
} else {
return 'undefined';
}
};
var result = compile("foo[0]");
console.log(result);
console.log(eval(result));
You need to use Proxy as explained, but it can ultimately be integrated into a class constructor
return new Proxy(this, {
set: function( target, name, value ) {
...}};
with 'this'. Then the set and get (also deleteProperty) functions will fire. Although you get a Proxy object which seems different it for the most part works to ask the compare ( target.constructor === MyClass ) it's class type etc. [even though it's a function where target.constructor.name is the class name in text (just noting an example of things that work slightly different.)]
So you're hoping to do something like
var whatever = MyClassInstance[4];
?
If so, simple answer is that Javascript does not currently support operator overloading.
Have a look at Symbol.iterator. You can implement a user-defined ##iterator method to make any object iterable.
The well-known Symbol.iterator symbol specifies the default iterator for an object. Used by for...of.
Example:
class MyClass {
constructor () {
this._array = [data]
}
*[Symbol.iterator] () {
for (let i=0, n=this._array.length; i<n; i++) {
yield this._array[i]
}
}
}
const c = new MyClass()
for (const element of [...c]) {
// do something with element
}