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
newcreates 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).
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:
class Productdefines an instance-field setup and a prototype method.new Product()creates theproductobject.- During creation,
idandpriceare added directly toproduct. Object.keys(product)finds the enumerable properties directly owned byproduct:idandprice.totalWithTaxis not directly onproduct; JavaScript finds it throughProduct.prototypewhen it is called.Object.getOwnPropertyNames(Product.prototype)lists the prototype's own properties, includingconstructorandtotalWithTax.
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
handleCreateorhandleDelete. - Testing: Verify that a class exposes a required method or that an object has expected data fields.
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
| API | Returns | Includes inherited properties? | Includes non-enumerable string keys? |
|---|---|---|---|
Object.keys(object) | Own enumerable string keys | No | No |
Object.getOwnPropertyNames(object) | All own string keys | No | Yes |
Object.getOwnPropertySymbols(object) | Own symbol keys | No | Yes |
Reflect.ownKeys(object) | All own string and symbol keys | No | Yes |
for...in | Enumerable string keys | Yes | No |
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.
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 = valuebelong to each instance. - Methods declared in a class body belong to the prototype.
- Members marked
staticbelong to the class constructor. Object.keys()returns only own, enumerable, string-keyed properties.- Filter
constructorwhen 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.
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.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Access Modifiers and static: public, private, protected, and Defaults
Learn how C# public, private, protected, and default access control visibility, and how static differs from instance members.