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:
// 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:
// 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!"How the new Keyword Works
The new keyword does several things automatically:
// 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. Creates a new empty object
{} - 2. Links the object to the constructor's prototype
- 3. Binds
thisto the new object - 4. Executes the constructor code
- 5. Returns the new object automatically
Prototype Methods
For memory efficiency, add methods to the prototype instead of inside the constructor:
// 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); // trueWhat If You Forget new?
Calling a constructor without new causes problems:
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;
}The instanceof Operator
Use instanceof to check if an object was created by a specific constructor:
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); // trueFactory Functions vs Constructors
Both create objects, but with different patterns:
// 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| Feature | Constructor Function | Factory Function | ES6 Class |
|---|---|---|---|
| Uses new | |||
| instanceof works | |||
| Prototype methods | Per instance | ||
| Memory efficient | Less | ||
| Private data | Closures | Closures | # prefix |
| Syntax clarity | Medium | Simple | Best |
Constructors vs ES6 Classes
ES6 classes provide cleaner syntax but work the same way under the hood:
// 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"Constructor Inheritance
You can set up inheritance between constructor functions:
// 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); // trueTest Your Knowledge
Constructor Functions Quiz
5 questionsWhat does the new keyword do?
Why should constructor function names start with a capital letter?
What happens if you call a constructor without new?
Why put methods on the prototype instead of inside the constructor?
What is the relationship between constructor functions and ES6 classes?
Coding Challenge
Create a TodoList constructor function with methods on the prototype. The list should support adding, completing, removing, and filtering todos.
// 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