×

Constructor Function in JavaScript

What is a Constructor Function?

Both regular functions and constructor functions are just JavaScript functions. The difference lies in how they are used and whether they create objects when called with new.

A constructor function in JavaScript is a special type of function used to create and initialize objects. It acts as a blueprint for creating multiple instances of similar objects.

Key Characteristics of a Constructor Function:

  1. A regular function that is called with the new keyword.
  2. Used to initialize object properties.
  3. By convention, named with a capitalized first letter (e.g., Person, Car).

Example:

function Person(name, age) { this.name = name; // Assign instance properties this.age = age; // Assign instance properties } const person1 = new Person("Alice", 25); const person2 = new Person("Bob", 30); console.log(person1.name); // "Alice" console.log(person2.age); // 30 console.log(person1 instanceof Person); // true

Adding methods to a constructor function

Inefficient Way (Defining Methods Inside Constructor)

function Person(name, age) { this.name = name; this.age = age; this.greet = function () { // BAD: Creates a new function for every instance return `Hello, my name is ${this.name}`; }; } const person1 = new Person("Alice", 25); const person2 = new Person("Bob", 30); console.log(person1.greet === person2.greet); // false (different function instances)

If you define a method inside the constructor, each instance will get its own copy, which is inefficient:

🚨 Problem: Every time a new Person object is created, a new greet function is created in memory.

Efficient Way (Using prototype for shared methods)

function Person(name, age) { this.name = name; this.age = age; } // Adding a shared method to the prototype Person.prototype.greet = function () { return `Hello, my name is ${this.name}`; }; const person1 = new Person("Alice", 25); const person2 = new Person("Bob", 30); console.log(person1.greet()); // "Hello, my name is Alice" console.log(person1.greet === person2.greet); // true (both share the same function)

Let's see why this approach is efficient.

Read more about __proto__ and prototype here

Why using .prototype is Better?

  1. Methods defined on Person.prototype are shared by all instances, reducing memory usage.
  2. JavaScript looks up methods through the prototype chain when calling them.

📌 Prototype Chain:

person1 --> Person.prototype --> Object.prototype --> null

NOTE: JavaScript uses a prototype-based inheritance system (not class-based like some other languages, although ES6 introduced class syntax which is syntactic sugar over prototypes).

Read about prototype chain here


The constructor Property

Every function in JavaScript automatically has a prototype object containing a constructor property along with [[Prototype]] (hidden).

console.log(Person.prototype.constructor === Person); // true console.log(person1.constructor === Person); // true

Ensures instances can always identify their constructor function.


Checking an Object’s Constructor

Use instanceof to check if an object was created by a constructor:

console.log(person1 instanceof Person); // true console.log(person1 instanceof Object); // true console.log([] instanceof Array); // true console.log([] instanceof Object); // true

instanceof checks if an object inherits from a constructor’s prototype.


Alternative: Using Object.create() Instead of new

If you don’t want to use new, you can manually set the prototype using Object.create().

const personPrototype = { greet: function () { return `Hello, my name is ${this.name}`; } }; const person1 = Object.create(personPrototype); person1.name = "Alice"; console.log(person1.greet()); // "Hello, my name is Alice"

Allows manual prototype setting without constructor functions.

Read how Object.create() works step by step here


Enforcing new Usage with new.target

Inside a constructor function, new.target refers to the function only if called with new.

function Person(name) { if (!new.target) { throw new Error("Must use 'new' with Person()"); } this.name = name; } const p1 = new Person("Alice"); // Works const p2 = Person("Bob"); // Error: Must use 'new' with Person()

NOTE: Using new.target prevents accidental function calls without new.


Constructor Function vs. Class Syntax (ES6)

ES6 introduced class syntax, which is syntactic sugar over constructor functions. Read more about classes in JavaScript here

Example:

class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { return `Hello, my name is ${this.name}`; } } const person1 = new Person("Alice", 25); console.log(person1.greet()); // "Hello, my name is Alice" console.log(person1 instanceof Person); // true

Key Differences:

  1. class syntax is more readable.
  2. Methods in class are automatically added to the prototype (no need to explicitly use Person.prototype).

What Happens When You Call new Circle()?

Example:

function Circle() {} const circle = new Circle();

Step-by-Step Breakdown:

  1. A new empty object is created internally:
    const circle = {}; // (internally)
  2. The prototype is set:
    circle.__proto__ = Circle.prototype;

This sets up the prototype chain, so circle can inherit methods defined on Circle.prototype

  1. Any properties or methods added using this inside the function are assigned directly to the new object and any properties or methods defined on Circle.prototype will be available to circle (via prototype chaining).

  2. The new object is returned.

Checking the Prototype Relationship:

console.log(circle.__proto__ === Circle.prototype); // true

Prototype Chain:

circle --> Circle.prototype --> Object.prototype --> null

Adding Methods to Circle.prototype

function Circle(radius) { this.radius = radius; } Circle.prototype.getArea = function () { return Math.PI * this.radius * this.radius; }; const smallCircle = new Circle(2); const bigCircle = new Circle(5); console.log(smallCircle.getArea()); // 12.57 console.log(bigCircle.getArea()); // 78.54 console.log(smallCircle.__proto__ === Circle.prototype); // true

Read more about __proto__ and prototype here


Method Overriding in Prototypes

Instances can override prototype methods if needed:

smallCircle.getArea = function () { return "Overridden method!"; }; console.log(smallCircle.getArea()); // "Overridden method!" console.log(bigCircle.getArea()); // 78.54 (still uses prototype)

Inspecting Circle.prototype

function Circle() {} console.log(Circle.prototype); // {} console.log(Circle.prototype.constructor === Circle); // true console.log(Object.getPrototypeOf(smallCircle) === Circle.prototype); // true console.log(smallCircle instanceof Circle); // true
  1. JavaScript automatically creates an object called Circle.prototype.

  2. This Circle.prototype object is used to define shared methods or properties that all instances of Person will inherit via their [[Prototype]] (aka __proto__).

  3. Circle.prototype.constructor points back to Circle.


What’s Inside an Instance circle?

Instance Properties (Directly on circle)

function Circle(radius) { this.radius = radius; // Own property this.describe = function () { // Own method return `Radius: ${this.radius}`; }; }

Prototype Methods (Inherited)

Circle.prototype.getArea = function () { return Math.PI * this.radius * this.radius; }; const circle = new Circle(5); console.log(circle.hasOwnProperty("getArea")); // false console.log("getArea" in circle); // true (inherited)

Checking Properties:

  1. Own properties:
console.log(Object.keys(circle)); // ["radius", "describe"]
  1. All properties (including inherited):
for (let key in circle) { console.log(key); // "radius", "describe", "getArea" }

NOTE: (Important)

  1. for...in loop Iterates over all enumerable properties, including both own properties and inherited properties (from the prototype chain).

  2. Object.keys(circle) Returns an array of the object's own enumerable properties (excluding inherited properties).

Internal Structure of circle:

{ radius: 5, describe: function() { ... }, __proto__: { getArea: function() { ... }, constructor: Circle } }

Difference Between Constructor Function and Regular Function

FeatureRegular FunctionConstructor Function
UsageCalled normallyCalled with new
PurposeExecutes codeCreates objects
Naming ConventioncamelCase (doSomething)PascalCase (Person)
Prototype LinkageNoYes
Return BehaviorReturns explicitlyReturns this (new object)

Q: Is Capitalization Mandatory for Constructor Functions?

No, but it’s a best practice to use PascalCase (e.g., Person) to indicate that the function should be called with new.


Summary

In JavaScript, constructor functions serve as blueprints for creating multiple objects with similar properties and methods. When invoked with the new keyword, these functions automatically create a new object, set its prototype to the constructor's prototype property, bind this to the new object, and return it. By convention, constructor functions are named with PascalCase (like Person or Car) to distinguish them from regular functions. For efficient memory usage, methods should be added to the constructor's prototype rather than defined inside the function itself, allowing all instances to share the same method references. The prototype chain enables inheritance, where objects can access properties and methods from their constructor's prototype. Modern JavaScript offers class syntax as a cleaner alternative, but under the hood, classes still use this constructor/prototype pattern. Understanding constructor functions is fundamental to grasping JavaScript's object-oriented capabilities and prototypal inheritance system.

This paragraph covers:

  1. The basic purpose and mechanism of constructor functions
  2. The new keyword's role
  3. Naming conventions
  4. Prototype-based method sharing
  5. The connection to modern class syntax
  6. The broader importance of the concept