Constructor Functions in JavaScript: Complete Guide

Constructor functions are the original way to create object factories in JavaScript. Learn how new works, what happens step by step, and how constructor functions relate to classes.

7 min read

A constructor function is a regular JavaScript function designed to be called with the new keyword. Its job is to create and initialize objects. Before ES6 classes existed, constructor functions were the standard way to build reusable object factories.

javascriptjavascript
function Person(name, age) {
  this.name = name;
  this.age = age;
}
 
Person.prototype.greet = function () {
  return `Hi, I am ${this.name} and I am ${this.age} years old.`;
};
 
const alice = new Person("Alice", 30);
console.log(alice.greet()); // "Hi, I am Alice and I am 30 years old."

Person looks like an ordinary function, but calling it with new changes everything. Inside the function, this refers to a brand new object, and properties assigned to this become own properties of that instance. Methods added to Person.prototype are shared across every instance instead of being copied.

What new Does, Step by Step

When you write new Person("Alice", 30), JavaScript performs four steps behind the scenes: create an empty object, link its prototype to Person.prototype, run the function with this set to that object, and return the result.

The four steps of the new operator

Each step maps onto plain JavaScript you could write yourself, which is a useful way to demystify what new actually does.

javascriptjavascript
// 1. const newObj = {};
// 2. Object.setPrototypeOf(newObj, Person.prototype);
// 3. Person.call(newObj, "Alice", 30);
// 4. const alice = newObj; (unless Person returns an object)

You can confirm these steps happened by checking the prototype relationship directly on an instance, which shows the link was set up correctly during construction.

javascriptjavascript
function Person(name) {
  this.name = name;
}
 
const p = new Person("Alice");
console.log(Object.getPrototypeOf(p) === Person.prototype); // true

The instance's prototype chain runs from p to Person.prototype to Object.prototype and finally to null. Methods placed on Person.prototype are found by walking that chain.

Adding Methods to the Prototype

Methods should go on Constructor.prototype, not inside the constructor body. If you define a method inside the constructor body instead, every single instance gets its own separate copy of that function, which wastes memory.

javascriptjavascript
function WrongPerson(name) {
  this.name = name;
  this.greet = function () { return `Hi, I am ${this.name}`; };
}
 
const w1 = new WrongPerson("Alice");
const w2 = new WrongPerson("Bob");
console.log(w1.greet === w2.greet); // false, different function objects

Moving the method to the prototype fixes that: every instance then shares one function instead of storing its own copy.

javascriptjavascript
function RightPerson(name) {
  this.name = name;
}
 
RightPerson.prototype.greet = function () {
  return `Hi, I am ${this.name}`;
};
 
const r1 = new RightPerson("Alice");
const r2 = new RightPerson("Bob");
console.log(r1.greet === r2.greet); // true, same function object

Placing methods on the prototype is more memory-efficient and lets you change a method after instances already exist; every instance sees the update because lookup happens through the chain each time.

The prototype and constructor Properties

Every function, except arrow functions, automatically gets a .prototype property when it is created. That property starts as a plain object with one own property, constructor, which points back to the function itself.

javascriptjavascript
function Dog(name) {
  this.name = name;
}
 
console.log(Dog.prototype.constructor === Dog); // true
 
const rex = new Dog("Rex");
console.log(rex.constructor === Dog); // true

Reading rex.constructor walks up the prototype chain and finds Dog.prototype.constructor, which points back to Dog, so you can use it to check what created an object. Be careful relying on this for critical logic, though: constructor is a writable property and can be overwritten, so it is not a guaranteed identity check.

Guarding Against Missing new

If someone calls a constructor function without new, this inside the function becomes undefined in strict mode or the global object in non-strict mode, so properties end up set on the wrong target entirely.

javascriptjavascript
function Person(name) {
  this.name = name;
}
 
const p = Person("Alice"); // forgot new
console.log(p); // undefined

You can guard against this with new.target, a special value that is only defined when the function is actually called with new.

javascriptjavascript
function Person(name) {
  if (!new.target) {
    throw new Error("Person must be called with new");
  }
  this.name = name;
}

Classes enforce this automatically. Calling a class without new throws a TypeError on its own, which is one reason classes are safer than raw constructor functions for new code.

Constructor Functions vs Classes

Classes are syntax sugar over constructor functions; every class is a constructor function under the hood with a cleaner syntax on top.

javascriptjavascript
function Car(make, model) {
  this.make = make;
  this.model = model;
}
Car.prototype.drive = function () {
  return `${this.make} ${this.model} is driving.`;
};

The class version below produces an equivalent object shape, with prototype wiring handled automatically by the class keyword instead of a manual assignment line.

javascriptjavascript
class Car {
  constructor(make, model) {
    this.make = make;
    this.model = model;
  }
  drive() {
    return `${this.make} ${this.model} is driving.`;
  }
}
FeatureConstructor FunctionClass
Requires new?Not enforced, manual guard neededEnforced, throws TypeError
MethodsManually added to .prototypeWritten in the class body
HoistingHoisted like regular functionsNot hoisted, temporal dead zone like let
Private fieldsNot supportedSupported with # syntax
extendsManual prototype chain setupBuilt-in extends keyword

Classes are the modern standard for new code because they are safer and more readable and have built-in support for private fields and static members. Understanding constructor functions still matters for reading older JavaScript and libraries that use them.

A Real Example: Building a Simple Model

Here is a constructor function that models a bank account with deposit and withdraw behavior and a running transaction history.

javascriptjavascript
function BankAccount(owner, initialBalance) {
  this.owner = owner;
  this.balance = initialBalance;
  this.transactions = [];
}
 
BankAccount.prototype.deposit = function (amount) {
  this.balance += amount;
  this.transactions.push({ type: "deposit", amount });
  return this.balance;
};

The withdraw method follows the same shape, adding a balance check before it changes any state so the account can never go negative.

javascriptjavascript
BankAccount.prototype.withdraw = function (amount) {
  if (amount > this.balance) return "Insufficient funds";
  this.balance -= amount;
  this.transactions.push({ type: "withdraw", amount });
  return this.balance;
};
 
const account = new BankAccount("Alice", 1000);
account.deposit(500);
account.withdraw(200);
console.log(account.balance); // 1300

Both methods live on BankAccount.prototype, so every account instance shares them while still keeping its own separate balance and transaction history. This same pattern powers a large share of pre-class JavaScript libraries and is the foundation that classes are built on. For the modern syntax, see JavaScript classes. For how this connects to inheritance, see the prototype chain.

Rune AI

Rune AI

Key Insights

  • A constructor function is any function called with the new keyword.
  • new creates a new object, sets its prototype to the function's .prototype, runs the function with this bound to that object, and returns it.
  • Add shared methods to Constructor.prototype, not inside the constructor body.
  • Every function (except arrow functions) has a .prototype property used only when the function is called with new.
  • Classes are syntax sugar over constructor functions and prototypes.
RunePowered by Rune AI

Frequently Asked Questions

Can I use an arrow function as a constructor?

No. Arrow functions do not have a .prototype property and cannot be called with new. Attempting to do so throws a TypeError. Use a regular function declaration, function expression, or class instead.

What happens if I forget new when calling a constructor?

In non-strict mode, this inside the function becomes the global object, so properties are set on globalThis instead of a new object. In strict mode or classes, this is undefined and accessing properties on it throws a TypeError. You can guard against this by checking new.target inside the function.

Are constructor functions still used, or should I always use classes?

Classes are the modern standard and preferred for new code. Constructor functions are still common in older codebases and libraries. Understanding them helps you read and maintain existing JavaScript. Classes are syntax sugar over constructor functions, so knowing both deepens your understanding of how JavaScript objects work.

Conclusion

A constructor function is a regular function called with new. Inside it, this refers to a fresh object whose prototype is set to the function's .prototype property. Methods added to the prototype are shared across all instances. While classes are now the preferred syntax, understanding constructor functions gives you a complete mental model of how object creation works at the language level.