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:
- A regular function that is called with the
new
keyword. - Used to initialize object properties.
- 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?
- Methods defined on
Person.prototype
are shared by all instances, reducing memory usage. - 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:
class
syntax is more readable.- 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:
- A new empty object is created internally:
const circle = {}; // (internally)
- The prototype is set:
circle.__proto__ = Circle.prototype;
This sets up the prototype chain, so circle can inherit methods defined on Circle.prototype
-
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). -
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
-
JavaScript automatically creates an object called
Circle.prototype
. -
This
Circle.prototype
object is used to define shared methods or properties that all instances of Person will inherit via their[[Prototype]]
(aka__proto__
). -
Circle.prototype.constructor
points back toCircle
.
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:
- Own properties:
console.log(Object.keys(circle)); // ["radius", "describe"]
- All properties (including inherited):
for (let key in circle) { console.log(key); // "radius", "describe", "getArea" }
NOTE: (Important)
-
for...in loop Iterates over all enumerable properties, including both own properties and inherited properties (from the prototype chain).
-
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
Feature | Regular Function | Constructor Function |
---|---|---|
Usage | Called normally | Called with new |
Purpose | Executes code | Creates objects |
Naming Convention | camelCase (doSomething ) | PascalCase (Person ) |
Prototype Linkage | No | Yes |
Return Behavior | Returns explicitly | Returns 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:
- The basic purpose and mechanism of constructor functions
- The
new
keyword's role - Naming conventions
- Prototype-based method sharing
- The connection to modern class syntax
- The broader importance of the concept