JavaScript Constructors
A constructor is a function whose job is to build new objects with a consistent shape and behavior. Instead of writing out the same object literal by hand every time you need a new user, product, or point on a graph, you write the blueprint once and let JavaScript stamp out as many instances as you need. Constructors are the foundation of object-oriented patterns in JavaScript, and understanding how they work under the hood — especially the new keyword and the prototype chain — will demystify a huge amount of how JavaScript objects behave.
Overview / How Constructors Work
In JavaScript, almost any function can act as a constructor. What makes a function a "constructor" is not special syntax (in the case of regular functions) but how it is called — specifically, whether it is invoked with the new keyword. By convention, constructor functions are named with an initial capital letter (Person, Car, Point) so that anyone reading the code immediately knows "this function is meant to be used with new."
ES6 introduced the class syntax, which includes an explicit constructor method. Under the hood, a class is still built on the same prototype-based object model — class is largely syntactic sugar over constructor functions and prototype assignment, with some extra guardrails (like requiring new and enforcing strict mode).
Every function created in JavaScript automatically gets a prototype property, which is a plain object. When you call a constructor with new, the newly created object’s internal [[Prototype]] is linked to that constructor’s prototype object. This is how instances share methods without each instance carrying its own copy: methods defined on Constructor.prototype live in one place in memory, and every instance can reach them through the prototype chain.
Syntax
There are two common ways to write a constructor:
function ConstructorName(param1, param2) {
this.property1 = param1;
this.property2 = param2;
}
class ConstructorName {
constructor(param1, param2) {
this.property1 = param1;
this.property2 = param2;
}
}
- ConstructorName — capitalized by convention, signaling it should be called with
new. - this — inside a constructor call, refers to the newly created object being built.
- constructor(…) — inside a
classbody, the special method that runs when an instance is created withnew. - new ConstructorName(…) — the operator that actually triggers object creation and runs the constructor body.
Examples
Example 1: A Basic Constructor Function
function Person(name, age) {
this.name = name;
this.age = age;
}
const alice = new Person('Alice', 30);
console.log(alice.name);
console.log(alice.age);
console.log(alice instanceof Person);
Alice
30
true
Calling new Person('Alice', 30) creates a fresh object, binds it to this inside the function body, and assigns the two properties onto it. The result is a full-fledged object that JavaScript recognizes as an instance of Person.
Example 2: Sharing Methods via the Prototype
function Car(make, model) {
this.make = make;
this.model = model;
}
Car.prototype.describe = function () {
return `${this.make} ${this.model}`;
};
const car1 = new Car('Toyota', 'Corolla');
const car2 = new Car('Honda', 'Civic');
console.log(car1.describe());
console.log(car2.describe());
console.log(car1.describe === car2.describe);
Toyota Corolla
Honda Civic
true
Notice that describe is defined once, on Car.prototype, rather than inside the constructor. Both car1 and car2 point to the exact same function in memory (that’s why car1.describe === car2.describe is true), which is far more memory-efficient than redefining a new function per instance.
Example 3: Class Constructors and Inheritance
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a noise.`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
return `${this.name} barks. (${this.breed})`;
}
}
const generic = new Animal('Creature');
const rex = new Dog('Rex', 'Labrador');
console.log(generic.speak());
console.log(rex.speak());
console.log(rex instanceof Animal);
Creature makes a noise.
Rex barks. (Labrador)
true
When a subclass has its own constructor, it must call super(...) before it can use this. This runs the parent class’s constructor first, ensuring the base properties (like name) are set up before the subclass adds its own (breed). Because Dog extends Animal, its instances also satisfy instanceof Animal — the prototype chain links Dog.prototype to Animal.prototype.
Under the Hood: What new Actually Does
When you write new ConstructorName(args), the JavaScript engine performs four steps:
- 1. Create. A brand-new, empty object is created.
- 2. Link. That object’s internal
[[Prototype]]is set toConstructorName.prototype, connecting it to the prototype chain. - 3. Bind and execute. The constructor function runs with
thisbound to the new object, executing whatever assignments and logic are in the body. - 4. Return. If the constructor explicitly returns an object, that object is returned instead of the newly created one. If it returns nothing, or returns a primitive (a string, number, boolean, etc.), the engine ignores that return value and returns the object built in steps 1–3 anyway.
This four-step dance is exactly what class constructors do internally too — class just enforces that you can’t call the function without new, and it runs in strict mode automatically, which closes off several footguns that plain constructor functions have.
Common Mistakes
Mistake 1: Forgetting the new keyword
function Person(name) {
this.name = name;
}
const bob = Person('Bob');
console.log(bob);
undefined
Without new, Person is just a normal function call. It returns nothing (undefined), and in non-strict mode this inside the call falls back to the global object instead of a new instance — silently leaking a name property onto the global scope rather than building the object you wanted. Class constructors avoid this trap entirely: calling a class without new throws a TypeError immediately, which is one reason many developers prefer class syntax.
The fix is simple — always use new:
function Person(name) {
this.name = name;
}
const bob = new Person('Bob');
console.log(bob);
Person { name: 'Bob' }
Mistake 2: Accidentally returning an object
function Point(x, y) {
this.x = x;
this.y = y;
return { x: 0, y: 0 };
}
const p = new Point(5, 10);
console.log(p);
{ x: 0, y: 0 }
Because the constructor explicitly returns an object, that object silently overrides the instance that new built. The p variable ends up as a plain { x: 0, y: 0 } object — not even an instance of Point — instead of the { x: 5, y: 10 } you probably intended. This is rarely done on purpose; it usually happens by accident, for example when a helper function meant to be called normally is mistakenly used as a constructor. The rule to remember: constructors should not return object values unless you deliberately want to override the created instance (a pattern occasionally used in factory-like code, but confusing otherwise).
Best Practices
- Capitalize constructor names (
Person, notperson) so it’s obvious at the call site thatnewis required. - Prefer
classsyntax for new code — it enforcesnew, runs in strict mode, and makes inheritance withextends/superfar less error-prone than manually wiring up prototypes. - Put methods on the prototype (or inside the
classbody, which does this automatically) rather than reassigning them inside the constructor, so instances share one copy instead of each carrying a duplicate function. - Avoid returning objects from a constructor unless you have a specific, well-documented reason to override the created instance.
- Use
instanceofto check whether an object was built by a particular constructor, e.g.rex instanceof Animal. - When extending a class, always call
super(...)before usingthisin the subclass constructor — the engine will throw aReferenceErrorif you accessthisfirst. - Keep constructors focused on initialization — assigning properties and validating inputs — rather than performing heavy computation or I/O.
Practice Exercises
- Exercise 1: Write a constructor function
Book(title, author, pages)that stores all three as properties, then add asummary()method on its prototype that returns a string like"Dune by Frank Herbert (412 pages)". - Exercise 2: Convert your
Bookconstructor from Exercise 1 into an ES6classwith the same behavior. - Exercise 3: Create a class
Employeewith a constructor that setsnameandsalary, then create a subclassManagerthat extendsEmployee, adds ateamarray property, and overrides adescribe()method to include the team size. Verify withinstanceofthat aManagerinstance is also anEmployee.
Summary
- A constructor is a function used with
newto build objects with a consistent shape. newcreates a new object, links it to the constructor’sprototype, runs the constructor withthisbound to that object, and returns it (unless the constructor explicitly returns another object).- Methods should live on the prototype (or in a
classbody) so all instances share one copy instead of duplicating functions. classsyntax is sugar over the same prototype model, but adds safety: it requiresnew, runs in strict mode, and makes inheritance withextends/supercleaner.- Forgetting
new, or accidentally returning an object from a constructor, are the two most common bugs — watch for both.
