Q108: Constructor Functions, Classes and Inheritance
What Interviewers Want To Evaluate
This question tests whether you understand how JavaScript creates object instances and shares behavior. Interviewers want constructor functions, new, prototypes, class, extends, super, instance fields, static methods, private fields, and composition trade-offs.
Senior answers should explain what class improves while still recognizing the prototype model underneath.
Short Interview Answer
Constructor functions and classes are ways to create objects that share behavior through prototypes. With constructor functions, methods are usually assigned to Constructor.prototype. With class, method definitions are placed on the class prototype automatically. extends connects prototype chains between classes, and super calls parent constructors or methods. JavaScript classes are useful syntax, but they still rely heavily on prototypes.
Detailed Interview Answer
Constructor functions are normal functions designed to be called with new.
function User(name) {
this.name = name;
}
User.prototype.greet = function greet() {
return `Hello ${this.name}`;
};
const user = new User("Asha");
The instance gets own data like name. Shared behavior like greet lives on the prototype.
What new Does
new performs a specific creation process.
create a new object
set the object's prototype to Constructor.prototype
call the constructor with this set to the object
return the object unless another object is explicitly returned
This is why calling a constructor without new is usually a bug.
Class Syntax
The equivalent class form is clearer:
class User {
constructor(name) {
this.name = name;
}
greet() {
return `Hello ${this.name}`;
}
}
const user = new User("Asha");
The greet method still lives on User.prototype.
Instance Fields
Modern JavaScript supports class fields.
class Counter {
count = 0;
increment() {
this.count += 1;
}
}
Fields are created on each instance. Methods are shared through the prototype.
Static Methods
Static methods belong to the class constructor itself, not instances.
class User {
static fromApi(raw) {
return new User(raw.name);
}
constructor(name) {
this.name = name;
}
}
User.fromApi({ name: "Asha" });
Static methods are useful for factories, parsers, helpers, and class-level utilities.
Private Fields
Private fields use # and are enforced by the language.
class Session {
#token;
constructor(token) {
this.#token = token;
}
hasToken() {
return Boolean(this.#token);
}
}
Private fields are not normal properties and cannot be accessed outside the class body.
Inheritance with extends
extends creates an inheritance relationship between classes.
class AdminUser extends User {
constructor(name, permissions) {
super(name);
this.permissions = permissions;
}
canDelete() {
return this.permissions.includes("delete");
}
}
super(name) calls the parent constructor. In derived classes, you must call super before using this.
Method Overriding
Child classes can override parent methods.
class AdminUser extends User {
greet() {
return `${super.greet()} with admin access`;
}
}
super.greet() calls the parent prototype method.
Prototype Chain Behind Classes
The class instance chain looks like this:
This is still prototype inheritance.
Composition vs Inheritance
Inheritance is useful when there is a stable "is-a" relationship. Composition is often better when behavior varies independently.
function createUser({ logger, permissions }) {
return {
can(action) {
logger.info(action);
return permissions.includes(action);
},
};
}
Composition keeps behavior explicit and avoids deep class hierarchies.
Common Mistakes
A common mistake is saying JavaScript classes work exactly like Java classes. They do not.
Another mistake is putting methods inside a constructor when they could be shared on the prototype.
function User(name) {
this.name = name;
this.greet = function greet() {
return `Hello ${this.name}`;
};
}
This creates a new function per instance.
Senior Trade-Offs
Classes can make domain models and framework APIs readable. They can also hide side effects, encourage inheritance-heavy design, and make testing awkward when dependencies are created internally.
Use classes where they clarify state and behavior. Use functions and composition where dependencies and behavior variants should remain explicit.
Debugging Workflow
When class behavior is confusing:
check whether constructor was called with new
inspect instance own properties
inspect prototype methods
check extends and super calls
check static vs instance method usage
check private field access
consider whether composition would be simpler
Interview Framing
Explain new and prototype method sharing first. Then describe how class, extends, and super make the pattern easier to write.
Revision Notes
Classes are syntax over prototype-based object construction with additional language features such as private fields and clearer inheritance syntax.
Key Takeaways
Constructor functions and classes both create objects with shared prototype behavior. The senior skill is choosing between inheritance and composition wisely.
Follow-Up Questions
- What does
newdo? - Where are class methods stored?
- What is the difference between instance and static methods?
- Why must derived classes call
superbefore usingthis? - How do private fields differ from normal properties?
- What is method overriding?
- When is inheritance useful?
- When is composition better?
- Why should methods usually not be recreated in constructors?
- How are classes related to prototypes?
How I Would Answer This In A Real Interview
I would start with constructor functions and new, show that methods are shared through prototypes, then map that model to class, extends, and super. I would close by saying that classes are useful, but composition often scales better for product code.