JavaScript Constructors

A constructor is a function whose job is to build new objects with a consistent shape and behavior. Instead of writing out the same object literal by hand every time you need a new user, product, or point on a graph, you write the blueprint once and let JavaScript stamp out as many instances as you need. Constructors are the foundation of object-oriented patterns in JavaScript, and understanding how they work under the hood — especially the new keyword and the prototype chain — will demystify a huge amount of how JavaScript objects behave.

Overview / How Constructors Work

In JavaScript, almost any function can act as a constructor. What makes a function a "constructor" is not special syntax (in the case of regular functions) but how it is called — specifically, whether it is invoked with the new keyword. By convention, constructor functions are named with an initial capital letter (Person, Car, Point) so that anyone reading the code immediately knows "this function is meant to be used with new."

ES6 introduced the class syntax, which includes an explicit constructor method. Under the hood, a class is still built on the same prototype-based object model — class is largely syntactic sugar over constructor functions and prototype assignment, with some extra guardrails (like requiring new and enforcing strict mode).

Every function created in JavaScript automatically gets a prototype property, which is a plain object. When you call a constructor with new, the newly created object’s internal [[Prototype]] is linked to that constructor’s prototype object. This is how instances share methods without each instance carrying its own copy: methods defined on Constructor.prototype live in one place in memory, and every instance can reach them through the prototype chain.

Syntax

There are two common ways to write a constructor:

function ConstructorName(param1, param2) {
  this.property1 = param1;
  this.property2 = param2;
}

class ConstructorName {
  constructor(param1, param2) {
    this.property1 = param1;
    this.property2 = param2;
  }
}
  • ConstructorName — capitalized by convention, signaling it should be called with new.
  • this — inside a constructor call, refers to the newly created object being built.
  • constructor(…) — inside a class body, the special method that runs when an instance is created with new.
  • new ConstructorName(…) — the operator that actually triggers object creation and runs the constructor body.

Examples

Example 1: A Basic Constructor Function

function Person(name, age) {
  this.name = name;
  this.age = age;
}

const alice = new Person('Alice', 30);

console.log(alice.name);
console.log(alice.age);
console.log(alice instanceof Person);
Alice
30
true

Calling new Person('Alice', 30) creates a fresh object, binds it to this inside the function body, and assigns the two properties onto it. The result is a full-fledged object that JavaScript recognizes as an instance of Person.

Example 2: Sharing Methods via the Prototype

function Car(make, model) {
  this.make = make;
  this.model = model;
}

Car.prototype.describe = function () {
  return `${this.make} ${this.model}`;
};

const car1 = new Car('Toyota', 'Corolla');
const car2 = new Car('Honda', 'Civic');

console.log(car1.describe());
console.log(car2.describe());
console.log(car1.describe === car2.describe);
Toyota Corolla
Honda Civic
true

Notice that describe is defined once, on Car.prototype, rather than inside the constructor. Both car1 and car2 point to the exact same function in memory (that’s why car1.describe === car2.describe is true), which is far more memory-efficient than redefining a new function per instance.

Example 3: Class Constructors and Inheritance

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    return `${this.name} makes a noise.`;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  speak() {
    return `${this.name} barks. (${this.breed})`;
  }
}

const generic = new Animal('Creature');
const rex = new Dog('Rex', 'Labrador');

console.log(generic.speak());
console.log(rex.speak());
console.log(rex instanceof Animal);
Creature makes a noise.
Rex barks. (Labrador)
true

When a subclass has its own constructor, it must call super(...) before it can use this. This runs the parent class’s constructor first, ensuring the base properties (like name) are set up before the subclass adds its own (breed). Because Dog extends Animal, its instances also satisfy instanceof Animal — the prototype chain links Dog.prototype to Animal.prototype.

Under the Hood: What new Actually Does

When you write new ConstructorName(args), the JavaScript engine performs four steps:

  • 1. Create. A brand-new, empty object is created.
  • 2. Link. That object’s internal [[Prototype]] is set to ConstructorName.prototype, connecting it to the prototype chain.
  • 3. Bind and execute. The constructor function runs with this bound to the new object, executing whatever assignments and logic are in the body.
  • 4. Return. If the constructor explicitly returns an object, that object is returned instead of the newly created one. If it returns nothing, or returns a primitive (a string, number, boolean, etc.), the engine ignores that return value and returns the object built in steps 1–3 anyway.

This four-step dance is exactly what class constructors do internally too — class just enforces that you can’t call the function without new, and it runs in strict mode automatically, which closes off several footguns that plain constructor functions have.

Common Mistakes

Mistake 1: Forgetting the new keyword

function Person(name) {
  this.name = name;
}

const bob = Person('Bob');

console.log(bob);
undefined

Without new, Person is just a normal function call. It returns nothing (undefined), and in non-strict mode this inside the call falls back to the global object instead of a new instance — silently leaking a name property onto the global scope rather than building the object you wanted. Class constructors avoid this trap entirely: calling a class without new throws a TypeError immediately, which is one reason many developers prefer class syntax.

The fix is simple — always use new:

function Person(name) {
  this.name = name;
}

const bob = new Person('Bob');

console.log(bob);
Person { name: 'Bob' }

Mistake 2: Accidentally returning an object

function Point(x, y) {
  this.x = x;
  this.y = y;
  return { x: 0, y: 0 };
}

const p = new Point(5, 10);

console.log(p);
{ x: 0, y: 0 }

Because the constructor explicitly returns an object, that object silently overrides the instance that new built. The p variable ends up as a plain { x: 0, y: 0 } object — not even an instance of Point — instead of the { x: 5, y: 10 } you probably intended. This is rarely done on purpose; it usually happens by accident, for example when a helper function meant to be called normally is mistakenly used as a constructor. The rule to remember: constructors should not return object values unless you deliberately want to override the created instance (a pattern occasionally used in factory-like code, but confusing otherwise).

Best Practices

  • Capitalize constructor names (Person, not person) so it’s obvious at the call site that new is required.
  • Prefer class syntax for new code — it enforces new, runs in strict mode, and makes inheritance with extends/super far less error-prone than manually wiring up prototypes.
  • Put methods on the prototype (or inside the class body, which does this automatically) rather than reassigning them inside the constructor, so instances share one copy instead of each carrying a duplicate function.
  • Avoid returning objects from a constructor unless you have a specific, well-documented reason to override the created instance.
  • Use instanceof to check whether an object was built by a particular constructor, e.g. rex instanceof Animal.
  • When extending a class, always call super(...) before using this in the subclass constructor — the engine will throw a ReferenceError if you access this first.
  • Keep constructors focused on initialization — assigning properties and validating inputs — rather than performing heavy computation or I/O.

Practice Exercises

  • Exercise 1: Write a constructor function Book(title, author, pages) that stores all three as properties, then add a summary() method on its prototype that returns a string like "Dune by Frank Herbert (412 pages)".
  • Exercise 2: Convert your Book constructor from Exercise 1 into an ES6 class with the same behavior.
  • Exercise 3: Create a class Employee with a constructor that sets name and salary, then create a subclass Manager that extends Employee, adds a team array property, and overrides a describe() method to include the team size. Verify with instanceof that a Manager instance is also an Employee.

Summary

  • A constructor is a function used with new to build objects with a consistent shape.
  • new creates a new object, links it to the constructor’s prototype, runs the constructor with this bound to that object, and returns it (unless the constructor explicitly returns another object).
  • Methods should live on the prototype (or in a class body) so all instances share one copy instead of duplicating functions.
  • class syntax is sugar over the same prototype model, but adds safety: it requires new, runs in strict mode, and makes inheritance with extends/super cleaner.
  • Forgetting new, or accidentally returning an object from a constructor, are the two most common bugs — watch for both.