JS Constructor Functions: A Complete Tutorial

Master JavaScript constructor functions — the original OOP pattern before classes. Learn how the new operator works, prototype-based method sharing, the constructor property, instanceof checks, and the relationship to modern class syntax.

JavaScriptintermediate
12 min read

Before ES6 classes arrived in 2015, constructor functions were the primary way to create objects with shared behavior in JavaScript. Even today, understanding constructor functions is essential because class syntax is built directly on top of them — classes are syntactic sugar, not a separate inheritance model. This tutorial covers constructor functions from first principles to their relationship with modern class syntax.

What a Constructor Function Is

A constructor function is a regular function invoked with the new keyword. By convention, constructor functions are named with a capital first letter (PascalCase) to distinguish them from regular functions:

javascriptjavascript
// Constructor function — PascalCase convention
function Person(name, age) {
  this.name = name;
  this.age = age;
}
 
// Called with new
const alice = new Person("Alice", 30);
console.log(alice.name); // "Alice"
console.log(alice.age);  // 30

Without new, this does not refer to a new object — in non-strict mode it refers to window/global, in strict mode it is undefined.

How the new Operator Works

The new operator does four things automatically:

  1. Creates a new empty object: {}
  2. Sets its [[Prototype]] to Constructor.prototype
  3. Executes the constructor with this bound to the new object
  4. Returns the new object (unless the constructor explicitly returns a different object)
javascriptjavascript
// What new does under the hood:
function simulateNew(Constructor, ...args) {
  // Step 1 + 2: Create object linked to prototype
  const obj = Object.create(Constructor.prototype);
  // Step 3: Call constructor with new object as this
  const result = Constructor.apply(obj, args);
  // Step 4: Return new object (or explicit object return)
  return result instanceof Object ? result : obj;
}
 
function Animal(type) {
  this.type = type;
}
 
const dog1 = new Animal("dog");
const dog2 = simulateNew(Animal, "dog");
console.log(dog1.type === dog2.type); // true

Adding Methods via the Prototype

Defining methods inside the constructor body creates a new function copy per instance. The correct pattern is adding methods to Constructor.prototype:

javascriptjavascript
function Person(name, age) {
  this.name = name;
  this.age = age;
  // WRONG: creates a new greet function for every Person instance
  // this.greet = function() { return `Hi, I'm ${this.name}`; };
}
 
// CORRECT: shared via prototype — all instances use one function object
Person.prototype.greet = function() {
  return `Hi, I'm ${this.name}, ${this.age} years old.`;
};
 
Person.prototype.birthday = function() {
  this.age += 1;
};
 
const alice = new Person("Alice", 30);
const bob = new Person("Bob", 25);
 
console.log(alice.greet()); // "Hi, I'm Alice, 30 years old."
console.log(bob.greet());   // "Hi, I'm Bob, 25 years old."
 
// Both instances share the exact same function object
console.log(alice.greet === bob.greet); // true

This is the foundation of prototype-based method sharing — a core concept in JavaScript's prototype chain.

The constructor Property

Every Constructor.prototype object has a constructor property pointing back to the constructor function:

javascriptjavascript
function Car(make) { this.make = make; }
 
console.log(Car.prototype.constructor === Car); // true
 
const myCar = new Car("Toyota");
console.log(myCar.constructor === Car);         // true (inherited via prototype)
console.log(myCar.constructor.name);            // "Car"

When you replace Constructor.prototype entirely (rather than augmenting it), the constructor pointer is lost and must be restored:

javascriptjavascript
function Vehicle(type) { this.type = type; }
 
// Replacing the entire prototype object — LOSES constructor
Vehicle.prototype = {
  describe() { return `A ${this.type}`; },
  // Missing constructor!
};
 
const v = new Vehicle("bus");
console.log(v.constructor === Vehicle); // false — broken!
console.log(v.constructor === Object);  // true — inherits Object.prototype.constructor
 
// Fix: restore the constructor property
Vehicle.prototype = {
  constructor: Vehicle, // ← Explicitly restore
  describe() { return `A ${this.type}`; },
};

instanceof and Prototype Linkage

instanceof checks whether a constructor's .prototype exists anywhere in the instance's prototype chain:

javascriptjavascript
function Dog(name) { this.name = name; }
function Animal() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
 
const rex = new Dog("Rex");
 
console.log(rex instanceof Dog);    // true
console.log(rex instanceof Animal); // true (via chain)
console.log(rex instanceof Object); // true (everything is)

instanceof checks the prototype chain, not constructor identity — it can be fooled by manual prototype manipulation.

Prototype Inheritance With Constructor Functions

Before classes, inheritance was established by linking prototype chains:

javascriptjavascript
function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function() {
  return `${this.name} makes a sound.`;
};
 
function Dog(name, breed) {
  Animal.call(this, name); // Call parent constructor to initialize inherited properties
  this.breed = breed;
}
 
// Link Dog.prototype to Animal.prototype
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // Restore constructor
 
// Override speak
Dog.prototype.speak = function() {
  return `${this.name} barks.`;
};
 
const rex = new Dog("Rex", "Labrador");
console.log(rex.speak());        // "Rex barks."
console.log(rex instanceof Dog); // true
console.log(rex instanceof Animal); // true

This is exactly what class / extends does under the hood. For more detail, see how prototypal inheritance works in JavaScript.

Constructor Functions vs Factory Functions

Factory functions are a no-new alternative:

javascriptjavascript
// Factory function — no new, no this, returns an object literal
function createPerson(name, age) {
  return {
    name,
    age,
    greet() { return `Hi, I'm ${name}`; },
  };
}
 
const alice = createPerson("Alice", 30);
console.log(alice.greet()); // "Hi, I'm Alice"
AspectConstructor FunctionFactory Function
Invocationnew PersonConstructor()createPerson()
instanceofWorks (prototype linkage)Broken (plain object)
this contextBound to new instanceNot needed
Prototype sharingVia Constructor.prototypePer-instance copy (memory cost)
Private state (closures)Awkward with prototype patternNatural via closure

Return Value Behavior

A constructor's return value is special:

javascriptjavascript
function Example() {
  this.x = 1;
  return { x: 999 }; // ← Returning an object — this replaces the new object!
}
function Example2() {
  this.x = 1;
  return 42; // ← Returning a primitive — ignored, new object returned
}
 
const e1 = new Example();
console.log(e1.x); // 999 — override with returned object
 
const e2 = new Example2();
console.log(e2.x); // 1 — primitive return ignored, normal behavior

Constructor Functions vs ES6 Classes

ES6 classes compile down to essentially the same constructor-function pattern:

javascriptjavascript
// Constructor function pattern
function Point(x, y) {
  this.x = x;
  this.y = y;
}
Point.prototype.toString = function() {
  return `(${this.x}, ${this.y})`;
};
 
// Equivalent ES6 class
class PointClass {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  toString() {
    return `(${this.x}, ${this.y})`;
  }
}
 
// Both produce the same prototype structure
const p1 = new Point(1, 2);
const p2 = new PointClass(1, 2);
console.log(p1.toString()); // "(1, 2)"
console.log(p2.toString()); // "(1, 2)"

Key difference: classes are not hoisted (use before declaration throws ReferenceError), and class bodies run in strict mode automatically. See JavaScript classes explained for the full class syntax breakdown.

Comparison Table: Constructor vs Class

FeatureConstructor FunctionES6 Class
Syntaxfunction Foo() {}class Foo {}
HoistingHoisted (declaration)Not hoisted (TDZ)
Strict modeOptionalAutomatic
Static methodsFoo.staticFn = fnstatic staticFn() {}
Private fieldsWorkarounds only#field syntax (ES2022)
InheritanceObject.create + callextends + super()
ReadabilityLowerHigher
Rune AI

Rune AI

Key Insights

  • new does four things: Creates an empty object, sets its [[Prototype]] to Constructor.prototype, runs the function with this bound to the object, and returns the object
  • Methods belong on the prototype: Defining methods in the constructor body duplicates them per instance; prototype methods are shared (one function object for all instances)
  • Replacing prototype breaks constructor: If you overwrite Constructor.prototype entirely, restore the constructor property manually: Constructor.prototype.constructor = Constructor
  • instanceof checks the chain: instanceof returns true if Constructor.prototype appears anywhere in the instance's prototype chain, not just the immediate prototype
  • Classes are constructor functions: ES6 class syntax compiles to the same prototype structure — understanding constructor functions means understanding how classes actually work
RunePowered by Rune AI

Frequently Asked Questions

Can I call a constructor function without new?

Yes, but you will typically get unexpected behavior — `this` will be the global object in sloppy mode, or `undefined` in strict mode. Add a guard to handle both cases: `if (!(this instanceof Person)) return new Person(name, age);`.

Why are methods placed on the prototype instead of in the constructor body?

Placing methods in the constructor body creates a new function object per instance, wasting memory. Adding to the prototype means all instances share one function object. For 1000 instances, prototype methods use 1 function object; in-constructor methods create 1000 separate function objects.

Is it still relevant to know constructor functions if I use classes?

bsolutely. Understanding constructor functions explains why `class` works the way it does, helps debug prototype chain issues, and is essential for reading older codebases (any code written before 2015 uses this pattern).

What is the difference between Object.create and new Constructor?

`Object.create(proto)` creates an object with a specific prototype but does NOT call any initialization function. `new Constructor()` creates the object, sets the prototype to `Constructor.prototype`, AND runs the constructor body. Both achieve prototype linkage but serve different purposes.

Conclusion

Constructor functions are the original OOP mechanism in JavaScript. The new operator creates an object, sets its prototype, and runs the constructor body. Methods are shared via Constructor.prototype rather than per-instance copies. The constructor property links instances back to their constructor. instanceof traverses the prototype chain for type checks. ES6 classes are clean syntax sugar over this exact pattern, so mastering constructor functions gives you deep understanding of how JavaScript's object system really works.