Question

Get Class Properties in JavaScript: Object Reflection Guide

csharp.netreflectionproperties

Question

How can I retrieve a list of all properties associated with a JavaScript class, including properties created on its instances and methods defined on its prototype?

Short Answer

JavaScript classes can expose properties in different places: on an instance, on the class constructor, or on the prototype. You will learn which reflection method to use for each location and how to distinguish data fields from methods.

Concept

JavaScript does not store every possible class property in one universal list.

A class is primarily syntax for creating a constructor function and a prototype. Where a member lives depends on how it was declared:

  • Instance fields are created when new creates an object.
  • Prototype methods are shared by all instances and live on ClassName.prototype.
  • Static fields and methods belong to the class constructor itself.
  • Inherited members may live higher in the prototype chain.

Reflection is the process of inspecting objects and their members at runtime. JavaScript provides built-in APIs such as Object.keys() and Object.getOwnPropertyNames() for this purpose.

This matters when building serializers, validation utilities, debugging tools, form generators, test helpers, and frameworks that need to inspect objects dynamically.

Mental Model

Think of a class as a building blueprint:

  • An instance field is furniture placed in each apartment when it is built.
  • A prototype method is a shared facility in the building, such as an elevator. Every apartment can use it, but it is not copied into each apartment.
  • A static member is information attached to the building company, not to an individual apartment.

To list members correctly, inspect the place where they are stored: the apartment (instance), shared facility area (prototype), or company record (class constructor).

Take Quiz

Syntax and Examples

Object.keys() returns an object's own enumerable string-keyed properties.

class User {
  role = "member";
  name = "Ada";

  greet() {
    return `Hello, ${this.name}`;
  }
}

const user = new User();

console.log(Object.keys(user));
// ["role", "name"]

console.log(Object.getOwnPropertyNames(User.prototype));
// ["constructor", "greet"]

role and name are instance fields, so they appear on user. The greet method is defined on User.prototype, so it does not appear in Object.keys(user).

To list static members, inspect the class itself:

class User {
  static type = "account";

  static createGuest() {
    return new User();
  }
}

console.log(Object.getOwnPropertyNames(User));
// Includes "length", "name", "prototype", "type", and "createGuest"

The built-in entries such as length, name, and prototype are normal properties of the constructor function. Filter them if your application only needs custom static members.

Step by Step Execution

Consider this code:

class Product {
  id = 101;
  price = 25;

  totalWithTax() {
    return this.price * 1.2;
  }
}

const product = new Product();

const fields = Object.keys(product);
const prototypeMembers = Object.getOwnPropertyNames(Product.prototype);

console.log(fields);
console.log(prototypeMembers);

Step by step:

  1. class Product defines an instance-field setup and a prototype method.
  2. new Product() creates the product object.
  3. During creation, id and price are added directly to product.
  4. Object.keys(product) finds the enumerable properties directly owned by product: id and price.
  5. totalWithTax is not directly on product; JavaScript finds it through Product.prototype when it is called.
  6. Object.getOwnPropertyNames(Product.prototype) lists the prototype's own properties, including constructor and totalWithTax.

Expected output:

[ 'id', 'price' ]
[ 'constructor', 'totalWithTax' ]

Real World Use Cases

  • API serialization: Select instance fields to convert a model to JSON before sending a response.
  • Validation: Inspect submitted object keys and reject fields that are not allowed.
  • Debugging: Log own properties of a model to understand its current state.
  • Form generation: Use a known list of data fields to create editable controls.
  • Plugin systems: Inspect a prototype for methods that follow a naming convention, such as handleCreate or handleDelete.
  • Testing: Verify that a class exposes a required method or that an object has expected data fields.
Take Quiz

Real Codebase Usage

In production code, developers usually inspect object properties for a specific purpose rather than trying to discover every possible member automatically.

Allow-list fields before serialization

function toPublicUser(user) {
  const allowedFields = ["id", "name"];

  return Object.fromEntries(
    Object.entries(user).filter(([key]) => allowedFields.includes(key))
  );
}

This is safer than returning every property, which could accidentally expose values such as password hashes or internal flags.

Check for a required method

function ensureSavable(value) {
  if (typeof value.save !== "function") {
    throw new TypeError("Expected an object with a save() method");
  }
}

This uses capability checking: the code verifies what it needs instead of depending on a particular class name.

Get custom prototype methods

function getMethods(ClassConstructor) {
  return Object.getOwnPropertyNames(ClassConstructor.prototype)
    .filter((name) => name !== "constructor")
    .filter((name) => typeof ClassConstructor.prototype[name] === "function");
}

Use this carefully. Methods inherited from parent classes are not included unless you intentionally walk the prototype chain.

Common Mistakes

Expecting Object.keys() to return class methods

class Counter {
  count = 0;

  increment() {
    this.count += 1;
  }
}

const counter = new Counter();
console.log(Object.keys(counter)); // ["count"], not ["count", "increment"]

increment is on Counter.prototype. Use Object.getOwnPropertyNames(Counter.prototype) to inspect it.

Inspecting the class when you need instance fields

class User {
  name = "Ada";
}

console.log(Object.keys(User)); // Does not contain "name"

User is the constructor. Create an instance first:

console.log(Object.keys(new User())); // ["name"]

Assuming every property is enumerable

Object.keys() excludes non-enumerable properties and symbol keys. Use Object.getOwnPropertyNames() for non-enumerable string keys, and Object.getOwnPropertySymbols() for symbol keys.

Forgetting inherited properties

Object.keys() and Object.getOwnPropertyNames() only inspect an object's own properties. If you need inherited methods, walk the prototype chain deliberately.

Treating property discovery as a type definition

Instance fields can be assigned conditionally or added later. Inspecting one instance does not guarantee that every future instance has exactly the same fields.

Comparisons

APIReturnsIncludes inherited properties?Includes non-enumerable string keys?
Object.keys(object)Own enumerable string keysNoNo
Object.getOwnPropertyNames(object)All own string keysNoYes
Object.getOwnPropertySymbols(object)Own symbol keysNoYes
Reflect.ownKeys(object)All own string and symbol keysNoYes
for...inEnumerable string keysYesNo

For most class inspection tasks:

  • Use Object.keys(instance) for normal instance data.
  • Use Object.getOwnPropertyNames(Class.prototype) for class methods.
  • Use Object.getOwnPropertyNames(Class) for static members.
  • Use Reflect.ownKeys() when symbol properties must also be included.
Take Quiz

Cheat Sheet

// Instance data fields
const instance = new MyClass();
Object.keys(instance);

// Instance fields, including non-enumerable string keys
Object.getOwnPropertyNames(instance);

// Prototype methods
Object.getOwnPropertyNames(MyClass.prototype);

// Custom prototype methods only
Object.getOwnPropertyNames(MyClass.prototype)
  .filter((key) => key !== "constructor")
  .filter((key) => typeof MyClass.prototype[key] === "function");

// Static members; includes built-in constructor properties
Object.getOwnPropertyNames(MyClass);

// String and symbol keys
Reflect.ownKeys(instance);

Rules to remember:

  • Fields declared with field = value belong to each instance.
  • Methods declared in a class body belong to the prototype.
  • Members marked static belong to the class constructor.
  • Object.keys() returns only own, enumerable, string-keyed properties.
  • Filter constructor when listing prototype methods.

FAQ

How do I list properties of a JavaScript object?

Use Object.keys(object) for its own enumerable string-keyed properties.

Object.keys({ id: 1, name: "Ada" });
// ["id", "name"]

Why does Object.keys(new MyClass()) not show methods?

Class methods are stored on MyClass.prototype, not copied to every instance. Use Object.getOwnPropertyNames(MyClass.prototype).

How do I get static properties from a class?

Inspect the constructor itself:

Object.getOwnPropertyNames(MyClass);

Filter built-in keys such as name, length, and prototype if needed.

Does Object.keys() include inherited properties?

No. It returns only enumerable properties directly owned by the object. A for...in loop can include enumerable inherited properties.

How can I list non-enumerable properties?

Use Object.getOwnPropertyNames(object). It still does not include symbol properties; use Reflect.ownKeys(object) for both strings and symbols.

Can I list fields before creating an instance?

Not reliably for normal instance fields. Those fields are created when the constructor runs, and they can depend on constructor arguments or conditions.

Should I use property inspection for validation?

It can help, but use an explicit allow-list or schema for important validation. Runtime-discovered properties alone are not a secure contract.

Take Quiz

Mini Project

Description

Build a small class-inspection utility for debugging model classes. It reports an instance's data fields, a class's custom prototype methods, and custom static members separately. This mirrors how tooling can inspect an object's structure without mixing these different kinds of members.

Goal

Create a function that prints a clear member report for a class and one of its instances.

Requirements

Create a class with at least two instance fields, two instance methods, and one static field. Create an instance of the class. List the instance's own enumerable fields. List prototype methods without including constructor. List custom static members while excluding common built-in function properties.

Take Quiz

Keep learning

Related questions

View more