6

given these two classes

class Foo{
  f1;

  get f2(){
    return "a";
  }
}

class Bar extends Foo {
  b1;

  get b2(){
    return "a";
  }
}

let bar = new Bar();

What code will get me this list of properties from the bar instance? ['f1', 'f2', 'b1', 'b2']

Here is a Babel sample


Update

This should be part of @Marc C's answer:

Using a decorator I can easily turn a non enumerable property into an enumerable property:

class Bar extends Foo {

  @enumerable()  
  get b2(){
    return "a";
  }

}

Here is the decorator source:

function enumerable() {
  return function(target, key, descriptor) {
    if (descriptor) {
      descriptor.enumerable = true;
    }
  };
}

Here is a Babel sample

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
Sylvain
  • 19,099
  • 23
  • 96
  • 145
  • This honestly seems like a dup of http://stackoverflow.com/questions/31054910/get-functions-of-a-class. There is no reason to make the properties enumerable. – loganfsmyth Jan 07 '16 at 22:11
  • 1
    [tag:babel]: *"Python internationalization library with an emphasis on web-based applications. For questions about the JavaScript library, please use [babeljs]."* – Felix Kling Jan 08 '16 at 01:31

2 Answers2

7

That's not valid syntax for declaring properties in a class. Instead, declare them in the constructor.

class Foo {
  constructor() {
    this.f1 = undefined;
  }
}

Then you can get them using Object.keys.

Using experimental features in Babel will allow you to declare properties using that syntax but their values must be declared.

class Foo {
  f1 = 0;
  ...
}

As for accessing the getters, getters are non-enumerable by default and can't be accessed using Object.keys or any similar mechanism. However, you can create enumerable getters using Object.defineProperty.

Object.defineProperty(bar, 'f2', {
  get() { 
    return "a"; 
  }
});

If you're using experimental ES7 features, you can apply a decorator to the class method and get the same behavior. See this Babel sample.

class Foo {
  @enumerable()
  get b2() {
    return "a";
  }
}

function enumerable() {
  return function(target, key, descriptor) {
    if (descriptor) {
      descriptor.enumerable = true;
    }
  }
}
Sylvain
  • 19,099
  • 23
  • 96
  • 145
Mike Cluck
  • 31,869
  • 13
  • 80
  • 91
  • 1 - I do use this experimental feature that babel supports (for `f1` and `b1`). 2 - `Object.keys()` does not get me `b2` and `f2`. – Sylvain Jan 07 '16 at 20:23
  • @Sylvain Babel may accept it but if you look at the compiled code, `f1` and `b1` are not declared. Babel will declare them if you assign them values such as `f1 = 0`. As for `f2` and `b2`, [getters are not enumerable by default](http://stackoverflow.com/questions/34517538/setting-an-es6-class-getter-to-enumerable). – Mike Cluck Jan 07 '16 at 20:28
  • good point. 1 - I can fix this (and leave it outside a constructor) if I set them to undefined up there. 2 - Is there a function that will get me the list of these getters or are they truly hidden? – Sylvain Jan 07 '16 at 20:35
  • @Sylvain They're truly hidden when written in that format. Using `Object.defineProperty`, you can make an enumerable getter and that will be visible. – Mike Cluck Jan 07 '16 at 20:42
  • Check my update above, if you think its a good idea, add it to your answer and I'll accept it (I did not dare update your edit your answer) – Sylvain Jan 07 '16 at 21:08
  • @Sylvain Interesting, I didn't know about ES7 decorators. Updated my answer to include them. Thanks for that. – Mike Cluck Jan 07 '16 at 21:18
  • One more thing, `Object.keys(bar)` does not work. I had to use `for (let p in bar)` to get all 4 properties. This should be added to your answer too. – Sylvain Jan 07 '16 at 21:31
  • You can easily get all the properties using `Object.getOwnPropertyNames`, even if they are non-enumerable. Making them enumerable is totally unrelated to being able to list them as long as you don't use `for...in`. See http://stackoverflow.com/questions/31054910/get-functions-of-a-class – loganfsmyth Jan 07 '16 at 22:13
  • I have to get `f1` and `f2` as well which are inherited from `Foo`; `Object.getOwnPropertyNames` will not return them. – Sylvain Jan 07 '16 at 22:31
  • As the answer in the question I linked to says, you need to traverse the prototype chain because `getOwnPropertyNames` only returns `own` properties. `for...in` traverses the prototype chain, you need to do that too. In this specific case, the properties are on the direct prototype `Object.getOwnPropertyNames(Object.getPrototypeOf(a))` – loganfsmyth Jan 08 '16 at 00:09
  • This is of course an old reply, but since it's the chosen answer I thought I'd add that there is a proposal (that's likely to pass) that adds class field declarations https://github.com/tc39/proposal-class-fields and there's a Babel plugin to handle this syntax https://babeljs.io/docs/en/babel-plugin-proposal-class-properties – Knight Yoshi Nov 23 '18 at 07:01
2

I feel like this was answered before. You can apply Object.getOwnPropertyNames to the instance and its prototypes:

function getAllPropertyNames(obj) {
  let names = [];
  do {
    names.push.apply(names, Object.getOwnPropertyNames(obj));
    obj = Object.getPrototypeOf(obj);
  } while(obj !== Object.prototype);
  return names.filter(name => name !== 'constructor');
}
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • This was the only answer that put me on the right track: var that = Object.getPrototypeOf(this); var props = Object.getOwnPropertyNames(that); just ... wow what a language js is :-O – kiwichris Jan 11 '22 at 03:19