Volume 2

Q108: Constructor Functions, Classes and Inheritance

Difficulty: SeniorFrequency: HighAnswer time: 8-12 minutes

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

  1. What does new do?
  2. Where are class methods stored?
  3. What is the difference between instance and static methods?
  4. Why must derived classes call super before using this?
  5. How do private fields differ from normal properties?
  6. What is method overriding?
  7. When is inheritance useful?
  8. When is composition better?
  9. Why should methods usually not be recreated in constructors?
  10. 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.