JavaScript Constructor Functions

Constructor functions are a traditional way to create objects in JavaScript. They use the new keyword to create instances with shared behavior through prototypes.

Try It Yourself

Experiment with constructor functions and instance creation:

index.js
// Constructor Function example

// Define a constructor (capital letter by convention)
function Person(name, age) {
  this.name = name;
  this.age = age;

  this.introduce = function() {
    return "Hi, I'm " + this.name + ", " + this.age + " years old";
  };
}

// Create instances with 'new'
const alice = new Person("Alice", 28);
const bob = new Person("Bob", 32);

console.log(alice.introduce());
console.log(bob.introduce());

// Check instance type
console.log("\nalice instanceof Person:", alice instanceof Person);

// Each instance is independent
alice.age = 29;
console.log("\nAlice age:", alice.age);
console.log("Bob age:", bob.age);

Basic Constructor Function

A constructor function is a regular function that creates and initializes objects. By convention, constructor names start with a capital letter:

javascript
// Constructor function (note the capital letter)
function Car(make, model, year) {
  // 'this' refers to the new object being created
  this.make = make;
  this.model = model;
  this.year = year;
  this.isRunning = false;

  // Methods
  this.start = function() {
    this.isRunning = true;
    console.log(this.make + " " + this.model + " started!");
  };

  this.stop = function() {
    this.isRunning = false;
    console.log(this.make + " " + this.model + " stopped!");
  };
}

// Create instances using 'new'
const myCar = new Car("Toyota", "Camry", 2023);
const yourCar = new Car("Honda", "Civic", 2022);

myCar.start();  // "Toyota Camry started!"
yourCar.start(); // "Honda Civic started!"
Naming Convention
Always start constructor function names with a capital letter (PascalCase). This signals to other developers that the function should be called with new.

How the new Keyword Works

The new keyword does several things automatically:

javascript
// What 'new' does behind the scenes:

function Person(name) {
  // 1. Creates empty object: {}
  // 2. Sets prototype: Object.setPrototypeOf({}, Person.prototype)
  // 3. Binds 'this' to the new object

  this.name = name; // 4. Your code runs

  // 5. Automatically returns 'this' (the new object)
}

// These are roughly equivalent:
const person1 = new Person("Alice");

// What happens under the hood:
function createPerson(name) {
  const obj = {};                                    // 1. Create object
  Object.setPrototypeOf(obj, Person.prototype);     // 2. Set prototype
  Person.call(obj, name);                           // 3-4. Call with this = obj
  return obj;                                        // 5. Return object
}
const person2 = createPerson("Bob");
  1. 1. Creates a new empty object {}
  2. 2. Links the object to the constructor's prototype
  3. 3. Binds this to the new object
  4. 4. Executes the constructor code
  5. 5. Returns the new object automatically

Prototype Methods

For memory efficiency, add methods to the prototype instead of inside the constructor:

javascript
// PROBLEM: Methods are recreated for each instance
function BadPerson(name) {
  this.name = name;
  // This creates a NEW function for EACH instance (wastes memory)
  this.greet = function() {
    return "Hello, I'm " + this.name;
  };
}

// SOLUTION: Put methods on the prototype (shared by all instances)
function GoodPerson(name) {
  this.name = name;
}

GoodPerson.prototype.greet = function() {
  return "Hello, I'm " + this.name;
};

GoodPerson.prototype.sayAge = function(age) {
  return this.name + " is " + age + " years old";
};

const alice = new GoodPerson("Alice");
const bob = new GoodPerson("Bob");

console.log(alice.greet()); // "Hello, I'm Alice"
console.log(bob.greet());   // "Hello, I'm Bob"

// Both share the SAME method
console.log(alice.greet === bob.greet); // true
Memory Efficiency
Methods on the prototype are shared by all instances. Methods inside the constructor create a new copy for each instance, wasting memory.

What If You Forget new?

Calling a constructor without new causes problems:

javascript
function User(name) {
  this.name = name;
}

// Correct: with 'new'
const user1 = new User("Alice");
console.log(user1.name); // "Alice"

// WRONG: without 'new'
const user2 = User("Bob");
console.log(user2);      // undefined!
// In non-strict mode, 'name' is added to global object!

// SOLUTION: Check for 'new' inside constructor
function SafeUser(name) {
  // new.target is defined only when called with 'new'
  if (!new.target) {
    throw new Error("SafeUser must be called with new");
  }
  this.name = name;
}

// Or auto-fix:
function AutoFixUser(name) {
  if (!(this instanceof AutoFixUser)) {
    return new AutoFixUser(name);
  }
  this.name = name;
}
Always Use new
Forgetting new can add properties to the global object or throw errors. Use new.target or instanceof checks for safety.

The instanceof Operator

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

javascript
function Animal(name) {
  this.name = name;
}

function Dog(name, breed) {
  Animal.call(this, name); // Call parent constructor
  this.breed = breed;
}

// Set up inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

const rex = new Dog("Rex", "German Shepherd");

// Check instance types
console.log(rex instanceof Dog);    // true
console.log(rex instanceof Animal); // true
console.log(rex instanceof Object); // true

// constructor property
console.log(rex.constructor === Dog); // true

Factory Functions vs Constructors

Both create objects, but with different patterns:

javascript
// FACTORY FUNCTION
function createPerson(name, age) {
  return {
    name,
    age,
    greet() {
      return "Hi, I'm " + this.name;
    }
  };
}

const person1 = createPerson("Alice", 25);
// No 'new' keyword needed

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

Person.prototype.greet = function() {
  return "Hi, I'm " + this.name;
};

const person2 = new Person("Bob", 30);
// Requires 'new' keyword

// Key differences:
console.log(person1 instanceof Object);  // true
console.log(person1.constructor === Object); // true (not createPerson!)

console.log(person2 instanceof Person);  // true
console.log(person2.constructor === Person); // true
FeatureConstructor FunctionFactory FunctionES6 Class
Uses new
instanceof works
Prototype methodsPer instance
Memory efficientLess
Private dataClosuresClosures# prefix
Syntax clarityMediumSimpleBest

Constructors vs ES6 Classes

ES6 classes provide cleaner syntax but work the same way under the hood:

javascript
// Constructor Function (old way)
function PersonOld(name, age) {
  this.name = name;
  this.age = age;
}

PersonOld.prototype.greet = function() {
  return "Hello, I'm " + this.name;
};

PersonOld.prototype.haveBirthday = function() {
  this.age++;
};

// ES6 Class (modern way - same thing under the hood)
class PersonNew {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    return "Hello, I'm " + this.name;
  }

  haveBirthday() {
    this.age++;
  }
}

// Both work the same way
const old = new PersonOld("Alice", 25);
const modern = new PersonNew("Bob", 30);

console.log(old.greet());    // "Hello, I'm Alice"
console.log(modern.greet()); // "Hello, I'm Bob"
Modern JavaScript
For new code, prefer ES6 classes over constructor functions. They offer cleaner syntax, built-in inheritance, and other modern features.

Constructor Inheritance

You can set up inheritance between constructor functions:

javascript
// Parent constructor
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  console.log(this.name + " makes a sound");
};

// Child constructor
function Dog(name, breed) {
  // Call parent constructor
  Animal.call(this, name);
  this.breed = breed;
}

// Set up prototype chain
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// Add/override methods
Dog.prototype.speak = function() {
  console.log(this.name + " barks!");
};

Dog.prototype.fetch = function() {
  console.log(this.name + " fetches the ball!");
};

const dog = new Dog("Rex", "German Shepherd");
dog.speak(); // "Rex barks!"
dog.fetch(); // "Rex fetches the ball!"

console.log(dog instanceof Dog);    // true
console.log(dog instanceof Animal); // true

Test Your Knowledge

Constructor Functions Quiz

5 questions
Question 1

What does the new keyword do?

Question 2

Why should constructor function names start with a capital letter?

Question 3

What happens if you call a constructor without new?

Question 4

Why put methods on the prototype instead of inside the constructor?

Question 5

What is the relationship between constructor functions and ES6 classes?

Coding Challenge

Build a TodoList Constructor
Medium

Create a TodoList constructor function with methods on the prototype. The list should support adding, completing, removing, and filtering todos.

Starter Code
// Challenge: Create a constructor function for a TodoList
// Requirements:
// - Constructor takes an optional array of initial todos
// - add(text) - adds a todo item { text, completed: false }
// - complete(index) - marks a todo as completed
// - remove(index) - removes a todo
// - getAll() - returns all todos
// - getPending() - returns only incomplete todos
// Put methods on the prototype!

function TodoList(initialTodos) {
  // Your code here
}

// Add prototype methods here

// Test your implementation
const myTodos = new TodoList();

myTodos.add("Learn JavaScript");
myTodos.add("Build a project");
myTodos.add("Get a job");

console.log("All todos:", myTodos.getAll());

myTodos.complete(0);
console.log("After completing first:", myTodos.getAll());

console.log("Pending:", myTodos.getPending());

myTodos.remove(1);
console.log("After removing:", myTodos.getAll());

Summary

  • Constructor functions create objects when called with new
  • The new keyword creates an object, binds this, and returns it
  • Naming convention: Use PascalCase (capital first letter)
  • Put methods on the prototype for memory efficiency
  • Forgetting new causes bugs - use new.target to check
  • instanceof checks if an object was created by a constructor
  • ES6 classes are preferred in modern JavaScript - same concept, cleaner syntax